Calling Haskell from C using GreenCard


Let's say you are working on a software project where different components of the system are written in different languages. Now one of your colleagues asks whether she can use your component (written as a Haskell library) from her component (written in C) for testing.

In this scenario, the controlling part of the application is in C, but it should be able to call out to Haskell from time to time, to perform some part of the computation. How can it be done?

We provide the necessary hooks in both C and Haskell. In essence, although C is supposedly in control, the program must start in Haskell (to initialise the heap and other runtime structures), before passing control over completely to C. Also, the initialisation phase must include a stage where C is given some handles on the Haskell functions it will later apply and evaluate.

The Haskell side is easy - just use GreenCard and pass functions across the boundary as stable pointers. For example:

    module Main where

    %fun registerFn :: Int -> String -> a -> IO ()
    %call (int arity) (string name) (stable fn)

    %fun handOver :: () -> IO ()

    main = registerFn 2 "plus"  ((+)::Int->Int->Int) >>
           registerFn 1 "mult5" ((5*)::Int->Int) >>
           handOver ()

The C side is also pretty straightforward. You need to write an appropriate implementation of registerFn which stores the Haskell values for later use. All Haskell stable pointers (whether functions or data) have a uniform representation as far as C is concerned: they are of type HaskellRef, which in practice should be treated as an abstract datatype. The C header file usr/local/lib/nhc98/greencard.h includes all the other facilities that are needed to deal with HaskellRefs - to build application closures and evaluate them, and to turn ordinary C values into HaskellRefs and vice versa.

----greencard.h----
    typedef ... HaskellRef;

    extern HaskellRef  buildClosure (int, HaskellRef*);
    extern void        eval (HaskellRef);
    extern HaskellRef  makeInt (int);
    extern int         unmakeInt (HaskellRef);
    extern HaskellRef  makeBool (int);
    extern int         unmakeBool (HaskellRef);
    extern HaskellRef  makeChar (char);
    extern char        unmakeChar (HaskellRef);

Note the following points:

  • The arguments to buildClosure() are: the arity of the function; and a pointer to a small block of memory containing the the correct sequence of HaskellRefs to form the application.
  • Why does eval() not return a value? Because its HaskellRef argument already contains the value - it just happens to be in a more usable form following the call.
  • Can I make and unmake any C values into HaskellRefs or must I use only int char and bool? No, you could use other types too, but I haven't written the make and unmake wrappers yet (because no-one has asked for them yet). Ask and you shall receive!

The latest updates to these pages are available on the WWW from http://www.haskell.org/nhc98/

26 January 1999
York Functional Programming Group
Malcolm.Wallace@cs.york.ac.uk