Dynamically link against library in GHCi

As of March 2020, School of Haskell has been switched to read-only mode.

This post reviews a small feature of GHCi version 7.10 in order to promote it: GHCi

  • It's now possible to use :set -l{foo} in GHCi to link against a foreign library after startup.

Before 7.10

Before 7.10 library flags had to be passed as command-line arguments to ghci. If we wanted to call zlibVersion from Haskell

#include <zlib.h>

const char * zlibVersion (void);

we would first create a Haskell file

{-# START_FILE Version.hs #-}
import Foreign.C.String

foreign import ccall "zlibVersion"
  c_zlibVersion :: IO CString

zlibVersion :: IO String
zlibVersion = do
  version <- c_zlibVersion
  peekCString version

and then load it with ghci

$ ghci -l{-hi-}z{-/hi-} Version.hs
ghci> :t c_zlibVersion 
c_zlibVersion :: IO CString
ghci> :t zlibVersion 
zlibVersion :: IO String
ghci> zlibVersion

z is the shared library we use which will load the shared library libz.so (further information).

Having to specify your libraries up front can be inconvenient if you want to load libraries mid-session or if your editor or IDE manages ghci's arguments.


In GHCi 7.10 one can write:

ghci> :set -l{-hi-}z{-/hi-}
ghci> :load Version.hs
ghci> zlibVersion 

Dynamically loading your own shared library

Quick example:

{-# START_FILE /tmp/succ.c #-}
#include <stdio.h>

int foo(int x)
  static int var = 0;

  var += x;

  printf("Hey %d\n", var);
  return x + 1;

compiled with

$ clang -fPIC -shared -o /tmp/libsucc.so /tmp/succ.c

and invoked with

ghci> import Control.Monad
ghci> import Foreign.C.Types 
ghci> :set -l{-hi-}succ{-/hi-} -L{-hi-}/tmp{-/hi-}
ghci> foreign import ccall foo :: CInt -> IO CInt
ghci> replicateM 5 (foo 10)
Hey 10
Hey 20
Hey 30
Hey 40
Hey 50
ghci> replicateM 4 (foo 3)
Hey 53
Hey 56
Hey 59
Hey 62