How to use Foreign Function Interface Part I: Basics

Samuel Barr
3 min readApr 3, 2019

--

Recently I’ve been working on a toy project which involves being able to both read in and write to a .wav sound file. Fortunately, I had previously written this functionality in C for a school project. I had heard of Haskell’s function interface before, and figured this would be a good opportunity to learn it.

What I found was a language feature which is at one time quite easy to use, while at the same time difficult to find out how to use. The specifics of how a .wav file works shouldn’t be important to understanding this article.

To give the setup, I have this functionality written in C, split across wave.h which defines the struct and wave_lib.c which contains the code for reading and writing a file:

//wave.h
typedef struct {
uint32_t sampleRate; // samples per second
uint32_t dataSize; // number of bytes of data
uint16_t numChannels; // number of channels per sample
uint16_t bytesPerChannel; // how many bytes each channel gets
char *data;
} WAVE;
//wave_lib.c
WAVE *readWave(char *fileName);
void writeWave(char *fileName, WAVE *w);

And my goal is to use this end up with this functionality in Haskell:

data Wave = Wave
{ sampleRate :: Int
, dataSize :: Int
, numChannels :: Int
, bytesPerChannel :: Int
, waveData :: [Char]
}
readWave :: String -> IO Wave
writeWave :: String -> Wave -> IO ()

If you have experience with the foreign function interface, you may be wincing at the types used above- we’ll address that soon enough.

Before we get to implementing the above functions, let’s begin with a simpler example. Let’s try and get this function in Haskell:

//fun.c
int addOne(int x)
{
return x+1;
}
//fun.h
int addOne(int);

Getting a function as simple as this running in Haskell is not too difficult.

-- Fun.hs
{-# LANGUAGE ForeignFunctionInterface #-}
import Foreign.C.Typesforeign import ccall "fun.h addOne" c_addOne :: CInt -> CIntaddOne :: Int -> Int
addOne = fromIntegral . c_addOne . fromIntegral

Let’s break down what we’re doing here:

  • We import the function with foreign import. The ForeignFunctionInterface language extension is necessary to use this feature.
  • ccall tells Haskell that we are importing a function from C, and to use the C function calling protocol
  • "fun.c addOne" tells Haskell to import the function addOne from the fun.c file. Note that a header file fan be used instead of C source code, and that the paths are relative to the Haskell source code
  • We call the function c_addOne :: CInt -> CInt. We use the types imported from Foreign.C.Types, as GHC can guarantee that they will have the same runtime representation as their C counterpart
  • We then wrap the function we imported from C into a function using Haskell types

This is a common design pattern when using the FFI. It’s important to note that it’s up to you to translate the C function header into a Haskell type. We could’ve just as easily imported the function as c_addOne :: CInt -> IO CDouble, or more incediously, perform IO in the C function and not declare that in it’s type. GHC is unable to figure out if you’re being honest!

Getting all this working with Stack/Cabal is easy. You just need to tell your build tool where you’re keeping your header files, and where your C source code is. In you .cabal file, add the following fields to the library section:

-- MY_PROJECT.cabal
library
c-sources:
src/fun.c
include-dirs:
src

Of course, you don’t need to put your C source code and header files in the src folder. Just as long as you tell your build tool where to find them, you’re all good!

In the next installment, we’ll go over Ptr, ForeignPtr, using structs, and hsc2hs.

--

--