This page contains a proposal to add anonymous FFI imports to GHC.
FFI imports give a way to execute foreign code from Haskell. Currently C and JavaScript foreign code is supported, as of GHC 7.8.
FFI imports are introduced as top-level definitions, e.g.
foreign import ccall "cos" c_cos :: CDouble -> CDouble
However, it is often useful to call code directly, without a top-level declaration. The obvious advantage is that we can call C/JavaScript libraries directly without defining bindings. This approach is already taken by Fay, a Haskell-like language compiling to JavaScript, where we can say
(ffi "foo(%1)" :: Text -> Char) "Hello!"
where foo
is a JavaScript function.
This also makes writing libraries interoperating generically with foreign code much easier. For example currently the language-c-inline
library needs some inconvenient TH machinery to refer to the C functions it creates on the fly when processing a module with inline C. This machinery would not be needed if we had anonymous FFI imports.
We propose to extend the language syntax so that anonymous FFI imports can appear in ordinary Haskell expressions, like so:
(foreign import ccall “cos” :: CDouble -> CDouble) 1
Doing so should amount to no change past the desugarer.
In Parser.y
, a new alternative is added to the exp production rule:
exp : ...
| 'foreign' 'import' fimport
fimport : callconv safety fanonspec
| calconv fanonspec
fanonspec : STRING '::' sigtypedoc
| '::' sigtypedoc
Where fimport
and fanonspec
are patterned against the already existing fdecl
and fspec
, respectively. It might be worth thinking about factoring out the common parts between top-level and anonymous FFI.
This allows to use all the existing top-level FFI import facilities in an anonymous manner, by reusing the existing FFI import syntax without the naming.
The code in TcForeign.hs
already handles type-checking of top-level FFI imports, and said code can be imported wholesale with little modification to type-check anonymous FFI imports. Obviously type-checking of anonymous FFI imports will not cause the binding of any name.
FFI imports are already desugared to a ordinary Haskell functions. For example the already mentioned
foreign import ccall "cos" c_cos :: CDouble -> CDouble
Is desugared to (eliminating the core background noise from the -ddump-ds
output)
c_cos :: CDouble -> CDouble
c_cos =
(\(x :: Double) -> D# $ {__pkg_ccall_GC main cos Double# -> State# RealWorld -> (# State# RealWorld, Double# #)} x realWorld#)
`cast`
(Sym CDouble -> Sym CDouble :: ((Double -> Double) ~# (CDouble -> CDouble)))
Thus, all that needs to be done is take the desugaring code in DsForeign.hs
and adapt it to produce the same code, inline, for anonymous FFI imports.
Ideally we’d like to be able to call foreign code whose location (be it an address for C or some string for JavaScript) is only known at runtime. An ad-hoc way to achieve this right now for C is libffi, which lets us interface with code following the C calling convention programmatically. However, using such methods from Haskell is always going to incur some overhead. For instance, the current bindings to libffi for Haskell receive the arguments to the C function to call in an Haskell list.
For this reason, after we implement anonymous FFI imports as described we plan to extend them to be able to accept the location of the code at runtime, as a standard Haskell expression.