In this blog post, I am going to show you how to link a C library in Haskell. Each step is kept as simple as possible in order to focus on the overall goal.

I will:

The code snippets can be found here.

The C program

The C program is going to be extremely simple. The main.c is calling a library function and printing the result:

// main.c

#include <stdio.h>
#include "liba.h"

int main(int argc, char *argv[]) {
	printf("main.c: main()\n");
	double a = liba_func(1.0); // 42
	printf("main.c: a = %f\n", a);
}

The library function liba_func() is provided by, who guessed it, liba.c. The latter refers to libb_func() in order to carry out its work.

// liba.c

#include <stdio.h>
#include "libb.h"

double liba_func(double i) {
	printf("liba.c:liba_func() side effects!\n");
	return i + 1.0 + libb_func(); // return i + 1 + 40
}
// libb.c

#include <math.h>

double libb_func() {
	return fmod(5, 2) + 39.0; // return 40
}

The corresponding (educational) Makefile:

# gcc -Wall:    Enable all warnings
# gcc -c:       Do not link
# gcc -g:       Add debug symbols
# gcc -lm:      Link to libm(ath)
# gcc -o:       Output file name
#
# Makefile $@:  The file name of the target of the rule
# Makefile $^:  The names of all the prerequisites

main: main.c liba.o libb.o
        gcc -g -Wall -o $@ $^ -lm

liba.o: liba.c
        gcc -g -Wall -c -o $@ $^

libb.o: libb.c
        gcc -g -Wall -c -o $@ $^

clean:
        rm -rf *.o main

The static libraries

We can convert lib*.o into static libraries using ar. This tool creates a so called archive file which is basically just a collection of object files. In our case, both archives each contain only one object file. For completeness, we’ll let gcc link again the static libraries instead of the object files for main, too.

 #
 # Makefile $@:  The file name of the target of the rule
 # Makefile $^:  The names of all the prerequisites
+#
+# ar -c:        Create the archive
+# ar -r:        Update existing files in an archive
+# ar -s:        Add or update an archive index

-main: main.c liba.o libb.o
+main: main.c liba.a libb.a
        gcc -g -Wall -o $@ $^ -lm

...

+liba.a: liba.o
+       ar -csr $@ $^
+
+libb.a: libb.o
+       ar -csr $@ $^

One can inspect the archive content with nm <file.a>. We see that libb.a defines the function (“symbol”) libb_func and expects the symbol fmod. The latter comes from math.h and the actual function is linked by gcc (see -lm in the Makefile’s main target).

$ nm libb.a

libb.o:
         U fmod
00000000 T libb_func

Merging the libraries

The obvious way to merge both static libraries is to create a single archive from both object files (i.e. ar -csr libab.a liba.o libb.o). However, I am going to assume the libraries come as is and the object files are not available. This allows me to show a trick with ar. The issue with ar is that you cannot simply create an archive by listing archive files instead of object files on the command line. ar will happily archive the input files without looking into them and thus create a nested archive. The inner archives are not accessible anymore:

$ ar -csr libab.a liba.a libb.a # create a new archive from the previous ones
$ nm libab.a
nm: liba.a: File format not recognized
nm: libb.a: File format not recognized

We can however ask ar to add the contents of one archive to the current one using the ADDLIB directive from GNU ar’s “librarien” compatibility mode. In this mode, ar is controlled with a script which we can also simply pipe from the command line.

$ echo 'CREATE libab.a\nADDLIB liba.a\nADDLIB libb.a\nSAVE\nEND' | ar -M
$ nm libab.a

libb.o:
         U fmod
00000000 T libb_func

liba.o:
00000000 T liba_func
         U libb_func
         U puts

This time, inspecting the archive yields the exported symbols (functions) as expected. The necessary Makefile changes:

 libb.a: libb.o
        ar -csr $@ $^

+libab.a: liba.a libb.a
+       echo 'CREATE $@\nADDLIB liba.a\nADDLIB libb.a\nSAVE\nEND' | ar -M
+

The Haskell program

The Haskell program is going to be straight forward. I’ll let stack create the a simply project for me

$ stack new haskell simple

The initial Main.hs:

module Main where

main :: IO ()
main = do
  putStrLn "hello world"

Linking to the C library

stack generates a Cabal file within the project. This file is kind of like the Makefile of a Haskell app. We can define GHC options within that file. In order to tell GHC to link to an external library, we have to give a library path and a library name (pretty similar to GCC). This is done by -L<path> and -l<libname> respectively. So we could simply use stack’s ghc-options parameter to add these. However, it is recommended to use extra-lib-dirs and extra-libaries instead. This is how we need to adapt the cabal file:

 executable haskell
   hs-source-dirs:      src
+--  ghc-options:         -L../c -lab -- don't do this
+  extra-lib-dirs:      ../c
+  extra-libraries:     ab
   main-is:             Main.hs
   default-language:    Haskell2010
   build-depends:       base >= 4.7 && < 5

Of course, ../c is the path to the aforementioned C program which contains libab.a. The file name’s prefix is striped so that libab becomes ab only.

Using the C library

We are going to make a wrapper Haskell library (file) that exposes the functions of the C library. Calling the actual C functions is done trough Haskell’s Foreign Function Interfarce. The source code is simple and listed below. You can google around to find lots of information about suing the FFI in more detail.

{-# LANGUAGE ForeignFunctionInterface #-} -- enabling the FFI

module LibAb where -- declaring the module

foreign import ccall "liba_func" c_liba_func :: Double -> Double -- importing the C function

liba_func :: Double -> Double
liba_func = c_liba_func -- wrapping the C function inside a Haskell function

Now that we have the Haskell function liba_func at our disposal, we can go ahead and call in Main.hs:

 module Main where

+import LibAb
+
 main :: IO ()
 main = do
-  putStrLn "hello world"
+  putStrLn "Please enter a number:"
+  d <- fmap (read::String->Double) getLine
+  putStrLn $ "The answer is: " ++ show (liba_func d)

This is the outcome:

$ stack build --exec haskell
... 
Please enter a number:
1.1
liba.c:liba_func() side effects!
The answer is: 42.1

Note how the pure Haskell function liba_func :: Double -> Double produces side effects! This is quite expected. The Haskell compiler cannot look into the compiled C functions and cannot differentiate pure from impure code. It merely links the foreign functions where asked to. The programmer needs to provide this information. In this case, it should have been liba_func :: Double -> IO Double to accommodate for the printf in the C function.

 module LibAb where -- declaring the module

-foreign import ccall "liba_func" c_liba_func :: Double -> Double -- importing the C function
+foreign import ccall "liba_func" c_liba_func :: Double -> IO Double -- importing the C function

-liba_func :: Double -> Double
+liba_func :: Double -> IO Double
 liba_func = c_liba_func -- wrapping the C function inside a Haskell function
 main = do
   putStrLn "Please enter a number:"
   d <- fmap (read::String->Double) getLine
-  putStrLn $ "The answer is: " ++ show (liba_func d)
+  a <- liba_func d
+  putStrLn $ "The answer is: " ++ show a

Done! You just learned how to combined C & Haskell! Well, at least some part of it.

You can retrace all steps by looking into the commit history of this Git repo.