Next Previous Contents

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 Type mapping.

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:

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

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 ()


Next Previous Contents