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