Haskell's Foreign Function Interface
January 01, 0001As part of a research project, I did a tiny bit of work to figure out the tiniest part of Haskell’s Foreign Function Interface today. This is just the tip of the iceburg, but I thought I would show off the extremely simple demo that I worked out. I present both the C and Haskell versions of a program that feeds “abc” and “def” into the standard Unix crypt()
function.
Source material comes from The FFI Cookbook and from Foreign.C.String
C, Venerable C
I did not do things in this order. However, for demonstration, we should start with the C operation. First, crypt()
has the data prototype `char *crypt(const char *key, const char *salt);’. The code to call it takes a mere eleven lines:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main (int argc, char **argv) {
char *res;
res = crypt("abc", "def");
printf("%s\n", res);
return EXIT_SUCCESS;
}
Compile this with gcc -o crypt crypt.c -lcrypt
. When I compile I get the warning crypt.c:7:9: warning: assignment makes pointer from integer without a cast [enabled by default]
, which I do not understand unless crypt()
is not actually present in unistd.h
. But, the code works correctly and as expected.
An important note: crypt()
apparently returns a pointer to an internal data structure that you MUST NOT deallocate. I read comments in the man page about a GNU version of crypt()
, crypt_r()
, that is re-entrant, which reenforces my belief. If you want to keep the result of crypt()
, be sure to copy it away, but otherwise do not allocate it. Running the program against valgrind indicates no leaked memory, which really solidifies this understanding for me.
Haskell FFI
The Haskell FFI was pretty easy to use, ultimately, and the cookbook really shows what we need to make this work. It takes a little bit more code, though, because we have to do the marshalling to and from C data types:
{-# LANGUAGE ForeignFunctionInterface #-}
module Main where
import Foreign.C
import Foreign.Ptr
import Foreign.Marshal.Alloc
foreign import ccall "crypt" c_crypt :: CString -> CString -> CString
crypt key str = do
keyC <- newCString key
strC <- newCString str
res <- peekCAString $ c_crypt keyC strC
free keyC
free strC
return res
main = do
crypt "abc" "def" >>= putStrLn
foreign import ccall
is some serious magic, but it pretty much amounts to the same as dlopen()
. The catch is that you absolutely must declare the data type. So, here is the structure:
foreign import ccall "<optional header file> <function name>" <haskell binding> :: <type>
According to the FFI cookbook, prepending the name of the function with c_ is simply a standard.
What I do not know is whether c_crypt should have been declared as above, or as c_crypt :: CString -> CString -> IO CString
. I am not yet clear on when a C call should be within the IO context and when it should not. I suspect that the compiler will not enforce anything and that it is up to me to make a (potentially unsafe) judgement call in the matter.
What’s the Point?
I want to do Haskell on the new Ubuntu software stack for tablets. This uses QML (though the rest of Ubuntu seems to still use GTK). I have found a QML library for Haskell, but it is out of date and does not work with the current QT 5. So, I can either recode it from scratch, or I can fully understand it and update it for the modern QT. In both cases, I need to learn the FFI. But I’ve needed to learn the FFI for a long time, anyway.
Haskell FFI by Savanni D'Gerinel is licensed under a Creative Commons Attribution-NonCommercial-SharAlike 3.0 Unported License. You can link to it, copy it, redistribute it, and modify it, but don't sell it or the modifications and don't take my name from it.