The previous section bodes ill for an automatic system that attempts to take C header files and automatically generate the ``right'' Haskell functions; C header files simply do not contain enough information.
The rest of this document describes how we approach the problem. The general idea is to start from the Haskell type definition for the foreign function, rather than the C prototype. The Haskell type contains quite a bit more information; indeed, it is often enough to generate correct interface code. Sometimes, however, it is not, in which case we provide a way for the programmer to express more details of the interface. All of this is embodied in a program called ``Green Card''.
Green Card is a Haskell pre-processor. It takes a Haskell module
as input, and scans it for Green Card directives (which are lines
prefixed by ``%
''). It produces a new Haskell module as output,
and sometimes a C module as well (Figure
fig:pic1
).
Green Card's output depends on the particular Haskell implementation
that is going to compile it. For the Glasgow Haskell Compiler (GHC),
Green Card generates Haskell code that uses GHC's primitive
ccall/casm
construct to call C. All of the argument
marshalling is done in Haskell. For Hugs, Green Card generates a C
module to do most of the argument marshalling, while the generated
Haskell code uses Hugs' primitive
construct to access the
generated C stubs. The output for nhc13 is somewhere in between,
marshalling is in part done by the generated C stubs and in
part by the generated Haskell code.
For example, consider the following Haskell module:
module M where
import Prelude hiding (sin)
import StdDIS
%fun sin :: Float -> Float
sin2 :: Float -> Float
sin2 x = sin (sin x)
Everything is standard Haskell except the %fun
line, which asks
Green Card to generate an interface to a (pure) C function sin
.
After the GHC-targeted version of Green Card processes the file, it
looks like this:
Only GHC aficionados will understand this code. The whole point of Green Card is that Joe Programmer should not have to learn how to write this stuff.
module M where
import Prelude hiding (sin)
import StdDIS
sin :: Float -> Float
sin f = unsafePerformIO (
_casm_ ``%r = sin(%0)'' f >>= \ f' ->
return f')
sin2 :: Float -> Float
sin2 x = sin (sin x)
The %fun
line has been expanded to a blob of gruesome
boilerplate, while the rest of the module comes through unchanged.
If Hugs is the target, the Haskell source file remains unchanged,
but the Hugs variant of Green Card would generate output that
implements sin
using Hugs' primitive
mechanisms for
calling C. For the nhc13 target, Green Card generates something
different again. Much of the Green Card implementation is, however,
shared between both variants.
An important point here is that the Haskell type signature
for sin
remains the same, regardless of the target that Green
Card has been instructed to generate code for. Green Card allows
you to abstract away from the low-level details of how foreign
function calls are implemented by the individual systems.
Program -> Declaration_1
...
Declaration_n n >= 1
Declaration -> Procedure
| '%const' [Var] [Var1 .. Varn]
| '%enum' [Var] Derivings
Type [Var1 .. Varn]
| '%dis' [Var1] [Var1 .. Varn] '=' dis
| '%#include' Filename
| '%prefix' Var
| '%C' ccode
Procedure -> [Signature] [call] [ccode] [result]
Signature -> '%fun' Var :: Type
Type -> Var
| Var Type
| Type '->' Type
| '(' Type_1, ... , Type_n ')' n >= 1
Call -> '%call' Dis_1 ... Dis_n
Result -> '%fail' Cexp Cexp [Result]
| '%end' Cexp
| '%result' Dis
Cexp -> anything up until '}' is encountered.
| Ccode
Ccode -> '%code' Var
| '%safecode' Var
Filename -> '<' Var '>'
| "Var"