8.4. Exposing Haskell functions

So far we've provided the Haskell programmer with ways of importing external functions into the Haskell world. The other half of the FFI coin is how to expose Haskell functionality to the outside world. So, dual to the foreign import declaration is foreign export:

topdecl 
  : ...
  ..
  | 'foreign' 'export' callconv [ext_name] varid :: prim_type

A foreign export declaration tells the compiler to expose a locally defined Haskell function to the outside world, i.e., wrap it up behind a calling interface that's useable from C. It is only permitted at the toplevel, where you have to specify the type at which you want to export the function, along with the calling convention to use. For instance, the following export declaration:

foreign export ccall "foo" bar :: Int -> Addr -> IO Double

will cause a Haskell system to generate the following C callable function:

HsDouble foo(HsInt arg1, HsAddr arg2);

When invoked, it will call the Haskell function bar, passing it the two arguments that was passed to foo().

8.4.1. Exposing Haskell function values

The foreign export declaration gives the C programmer access to statically defined Haskell functions. It does not allow you to conveniently expose dynamically-created Haskell function values as C function pointers though. To permit this, the FFI supports dynamic foreign exports:

topdecl 
  : ...
  ..
  | 'foreign' 'export' [callconv] 'dynamic' varid :: prim_type -> IO Addr

A foreign export dynamic declaration declares a C function pointer generator. Given a Haskell function value of some restricted type, the generator wraps it up behind an externally callable interface, returning an Addr to an externally callable (C) function pointer.

When that function pointer is eventually called, the corresponding Haskell function value is applied to the function pointer's arguments and evaluated, returning the result (if any) back to the caller.

The mapping between the argument to a foreign export dynamic declaration and its corresponding C function pointer type, is as follows:

typedef cType[[Res]] (*Varid_FunPtr)
        (cType[[Ty_1]] ,.., cType[[Ty_n]]);

where cType[[]] is the Haskell to C type mapping presented in Section 8.2.5.

To make it all a bit more concrete, here's an example:

foreign export dynamic mkCallback :: (Int -> IO Int) -> IO Addr

foreign import registerCallback :: Addr -> IO ()

exportCallback :: (Int -> IO Int) -> IO ()
exportCallback f = do
  fx <- mkCallback f
  registerCallback fx

The exportCallback lets you register a Haskell function value as a callback function to some external library. The C type of the callback that the external library expects in registerCallback(), is: [1]

typedef HsInt (*mkCallback_FunPtr) (HsInt arg1);

Creating the view of a Haskell closure as a C function pointer entails registering the Haskell closure as a 'root' with the underlying Haskell storage system, so that it won't be garbage collected. The FFI implementation takes care of this, but when the outside world is through with using a C function pointer generated by a foreign export dynamic declaration, it needs to be explicitly freed. This is done by calling:

void freeHaskellFunctionPtr(void *ptr);

In the event you need to free these function pointers from within Haskell, a standard 'foreign import'ed binding of the above C entry point is also provided,

Foreign.freeHaskellFunctionPtr :: Addr -> IO ()

8.4.2. Code addresses

The foreign import declaration allows us to invoke an external function by name from within the comforts of the Haskell world, while foreign import dynamic lets us invoke an external function by address. However, there's no way of getting at the code address of some particular external label though, which is at times useful, e.g. for the construction of method tables for, say, Haskell COM components. To support this, the FFI has got foreign labels:

foreign label "freeAtLast" addrOf_freeAtLast :: Addr

The meaning of this declaration is that addrOf_freeAtLast will now contain the address of the label freeAtLast.

Notes

[1]

An FFI implementation is encouraged to generate the C typedef corresponding to a foreign export dynamic declaration, but isn't required to do so.