Most of the world's software is not written in Haskell. Not earth shattering news this, so how can we `improve' on this situation? There's (at least) two alternative routes that can be followed to make a language like Haskell a viable alternative when picking the language(s) to use for a project:
The first route is a very long one, and it often turns out that reverse engineering a library in Haskell doesn't radically improve on the original programming interface. The Haskell variant of the "not-invented-here" syndrome.
The second option makes much more sense, re-using external libraries from Haskell, customising their programming interfaces to provide a veneer that is more appealing to the Haskell Programmer In The Street.
Support for the Wise Choice is provided by most Haskell systems
through their foreign function interfaces. One early example of
such a beast was (and still is) GHC's support for embedding calls to C
functions inside IO code with the _ccall_
construct. Together with built-in compiler support for converting from
a primitive set of Haskell types to their expected external
representation (and back), this gives the Haskell programmer the
ability to interface to external libraries without too much bother.
Examples of libraries/applications that have been built using
_ccall_ include interfaces to windowing systems/toolkits (e.g.,
Haggis, Budgets, etc.), system libraries (e.g., POSIX lib), etc.
Other Haskell systems provide similar foreign function interface
support (e.g., Hugs and Gofer's primitive declarations.)
This is all good stuff and a move in generally the right direction, but current foreign function interfaces fall short in couple of departments:
_ccall_ is
inherently GHC-specific, so compiling such code with another compiler
breaks. Having a common FFI language/mechanism would be a Good Thing
from a user's point of view.
_ccall_ is restricted to the set of types
supported by C. This means that for Haskell data types that are not
part of this set have to be explicitly packed and unpacked to one of
the `C-callable' types by the programmer prior to calling out using
_ccall_. Writing this out is tedious and error prone.
Based on the experiences with GHC's _ccall_ and Hugs'
primitive,
Green Card was implemented, a foreign function interface
preprocessor for Haskell. It addresses the problem of portability by
providing a little language for describing the type signatures of
external functions, and how to convert from the Haskell data
representation of the external function to the expected
representations (and back again.) Given such a specification, the
implementation of Green Card takes care of generating the Haskell
system specific code for actually calling out.
Green Card has proven to be a very useful addition to the Haskell programmer's toolset, but it too has its problems and limitations:
%fun sin :: Double -> Double
where the external function sin expects a double as argument, and
gives one back. However, suppose the external function returns a pair
of results:
%fun lookupVal :: String -> IO (Bool, Int)
where the external function lookupVal returns a lookup status
value together with the matching integer (if any.) To write this using
Green Card, you'll need to use a combination of inline C code and
the Green Card's little language for marshalling values between the C and
Haskell worlds:
%fun lookupVal :: String -> IO (Bool, Int)
%call (string key)
%code int res; BOOL status;
% status = lookup(key, &res);
%result (bool status, int res)
setting up the call in C, and taking care of converting from the
intermediate C variables holding the results into Haskell result
returned.
Another example is when you want provide a Haskell binding to an
external function, but with some of its arguments fixed:
%fun createWindow :: String -> IO Window
%call (string title)
%code WINDOW w = CreateWindow(title, 100, 100, 200, 200);
%result (window w)
which also requires you to write some inline C code to set this up.
When there isn't an exact match between the desired Haskell signature
of an external function and its real signature, the programmer is
forced to make up the difference using a combination of inline C and
make use of Green Card's little language for describing how data
should be (un)packed and combined when going between Haskell and C
code.
Using C to do the impedance matching is powerful, but Green Card
doesn't give you any other options. Building a domain-specific
language with an (increasing) number of features and abstractions is
also unfortunate, as we've already got a programming language at hand
(Haskell) that we might as well be using for this.
typedef struct tagFoo {
int x;
int y;
double z;
} Foo;
requires you to define a corresponding Haskell data type and a
data interface scheme (DIS) which instructs Green Card
how to generate code that maps to/from the two representations
of the record type.
Instead of extending Green Card to address the above points, we've decided to opt for an off-the-shelf solution to the problem: using an interface definition language(IDL) to describe foreign functionality. In the cases where there is a difference between the type signature of the external function, and the type signature that the programmer wants to present to the Haskell user, Haskell is used to make up the difference.