Preamble
Haskell’s FFI is rather mission-critical for writing real-world applications. While GHC support for it is fine1, on the community level there is both little guidance on the matter and some rather daunting tooling problems. The result is a fractured ecosystem where every person has a different approach to FFI and it is far easier to hardcode a C library into your project than it is to make bindings to one.
1. Other than perhaps the foreign import
unsafe
qualifier. The person using the library typically knows better whether they want the safe
or unsafe
variant.
Definitions
A foreign library is a library written in a programming language that isn’t Haskell.
An FFI library is a Haskell library containing the definitions required to interface with a foreign library.
FFI libraries
At the centre of the proposal lies a concept of an FFI library, which I believe to be sufficiently different from any other kind of a Haskell library to warrant both a separate name and broad ecosystem support.
An FFI library contains an unsafe, loosely typed, minimal set of definitions required to interface with a foreign library. The names for the definitions should match the respective ones in the foreign library as close as possible to avoid confusion and simplify search. The definitions generally fall into the following types:
-
Function imports for functions that can be marshalled out of the box.
safe
qualifier should be used unless the function isunsafe
-only (e.g. specialized math algorithms).unsafe
qualifier, if needed, should be exported in a copy of a function with_unsafe
appended to it (orUnsafe
if the function name is in camel case). -
Types for functions that cannot be marshalled.
While useless for writing code, these will be useful in documentation. Higher-level libraries are expected to create their own foreign language functions to import such definitions.
-
Types for function callbacks.
Some of these will also be unmarshallable, ditto the previous point.
-
Type synonyms.
newtype
s should be discouraged, as they tend to clutter code more than help with safety. -
Constants.
Integer constants can be embedded nicely with
PatternSynonyms
(seegl
library), strings can be embedded into pointers withMagicHash
(GHC.Ptr.Ptr "honk"#
). Constants should be typed as loosely as the type system allows since foreign libraries may reuse constant names in different contexts. -
Information for accessing structured data, arranged around an uninhabited type.
For C
struct
s specifically this is the total size of thestruct
, it’s alignment and the offset of every single field. Note thatStorable
is not a good abstraction here and this will thus require extra type classes in a separate library.
The rules are not set in stone and libraries should be able to break them, the spirit of the approach is far more important here.
Curiously enough this means an FFI library is also -XSafe
, perhaps with the exception of foreign imports that return pure types (if those are even useful, i.e. don’t behave exactly like unsafePerformIO f
).
Cabal
.cabal
files have a rather curious problem: they don’t properly support foreign libraries. Foreign libraries are not the same as custom foreign code: the user should be tasked with installing a foreign library, not the FFI library maintainer.
See [RFC] Separate linking for an attempt to spec it out from my side.
In short, the goal is to have foreign libraries as a distinct class, which Haskell packages can then depend on alongside regular Haskell packages (and to have dependency trees in a similar way). The user would then be able to specify how to find the installed foreign libraries and their headers in their cabal.project
.
The definition of an FFI library itself in a .cabal
file should be mostly cosmetic, as they build and run same as any other Haskell library. Extra metadata fields would make sense for interoperability with other tools.
Haddock
No need for an overhaul here, merely a few custom metadata tags would allow alternative representations for FFI definitions. Structured data, while merely an empty datatype with a bunch of instances, could instead be displayed in proper language form.
Hackage
It would be nice for Hackage to take foreign library dependencies from Cabal files and display them on package webpages. That way users would know the package will not work out of the box unless all the foreign libraries are present.
Note that foreign library dependencies are not the same as regular ones: version ranges here are merely for user convenience and transitive dependencies should propagate.
I don’t know whether FFI libraries warrant a separate namespace, though if any foreign library is expected to have only one FFI library to it, this would be expected. Aforementioned extra Cabal metadata could be used to display the type of the library and to stylize the webpage accordingly if needed.
Automation
As I explained in the precursor comment, I do not believe in automatic interface generation, however a custom templating engine would make total sense for FFI libraries. I will not bother to speculate as to how it should look until the proposal is agreed upon and implemented.