Update: Incorporated changes by Adam Turoff.
I played with Haskell FFI today. It was mostly straight forward, but to save someone some time I present you with the simplest example of calling into a C library you wrote. You can download the whole working tarball from here.
Most languages provide some FFI to bridge to allow you to call at least C libraries. Some languages, like Ruby and Python1, have very elaborate hooks into C, because they themselves are written in C.
Haskell is written in Haskell (strange enough) and does not require that your C library knows anything about Haskell. You write the library and expose it as one. In Haskell you can then do a “foreign import” and have that function available for use in Haskell code. The definitive reference for all of this is: The Haskell 98 Foreign Function Interface 1.0.
We will be calling this C function:
void buzz(int array_length, int array);
The Haskell code that imports this function is the following:
foreign import ccall "buzzlib.h buzz" my_buzz :: Int -> Ptr (Int) -> IO ()
The only extension that needed to made to the Haskell standard was the introduction of the word “foreign” in the above statement. Broken down, this import statement does the following:
- buzzlib.h is where the function is declared and buzz is what the function is called.
- We then import it into our world as my_buzz
- “Int” is the first argument (array_length) and “Ptr (Int)” refers to a pointer to an array of integers.
let second_argument_to_buzz = [ 1, 2, 3, 4, 5 ] :: [Int] first_argument_to_buzz = length second_argument_to_buzz ptr <- newArray second_argument_to_buzz
The first two lines declare the values that we will be passing to the external function. Then we marshall the list “second_argument_to_buzz” into something we can pass to the C function.
newArray allocates enough memory for values, and then “pokes” the contents of the “second_argument_to_buzz” list into that area of memory and returns a pointer.
We are now ready to finally call our function:
my_buzz first_argument_to_buzz ptr free ptr
We can pass the first value as is, because it is a primitive value. The second value we had to first marshall as we have done in the previous snippet of code and we now have to send the pointer to the external library.
We then have to free that block of memory once we are done with it.
Adam Turoff simplified this a lot, unfortunately I lost the comments to this article. The simplified version of the call is:
withArray second_argument_to_buzz_function (my_buzz first_argument_to_buzz_function)
The only remaining question is how to actually compile all of this. For that, have a look at the Makefile in the archive.
1 I prefer Ruby’s FFI over Python’s. Granted, I last did a C library for Python in the 1.6 days.