Google

NAME="GENERATOR" CONTENT="Modular DocBook HTML Stylesheet Version 1.64 ">

4. Overview of Green Card

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,


 
  +- - - - - +       +- - - - +        +- - - - -+
  | Haskell' |       | Green  |        | Haskell |
  |  module  + - - ->| Card   +- - - ->| module  |
  +- - - - - +       +- -+- - +        +- - - +- +
                         |                    |              
                         |                    /
                         |              +- - - - - +
                         +- - - - - - ->|  C file  |
                                        +- - - - - +

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: [1]

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.

Notes

[1]

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.