REVISED: Friday, October 25, 2024
Haskell Foreign Function Interface (FFI).
I. HASKELL
The Haskell Foreign Function Interface (FFI) is the means by which Haskell code can use, and be used by, code written in other computer languages. Binding one language to another is a non-trivial task. The binding language needs to understand the calling conventions, type system, data structures, memory allocation mechanisms and linking strategy of the target language, just to get things working. The task is to carefully align the semantics of both languages, so that both languages can understand the data that passes between them.
II. FOREIGN LANGUAGE BINDINGS
The most common operation we will want to do is to call a C function from Haskell. So let us do that, by binding to some functions from the standard C math library. We will put the binding in a source file, and then compile it into a Haskell binary that makes use of the C code.
A. ENABLE FFI EXTENSION
To start with, we need to enable the FFI extension, as the FFI addendum support is not enabled by default. We do this via a LANGUAGE pragma at the top of our source file. GHC supports pragmas, or instructions to the compiler placed in the Haskell source code.
{-# LANGUAGE ForeignFunctionInterface #-} -- FFI Pragma
B. IMPORT FOREIGN MODULES
The next step is to import the Foreign modules, which provide useful types (such as pointers, numerical types, arrays) and utility functions, such as malloc and alloca, for writing bindings to other languages.
import Foreign
import Foreign.C.Types
C. CALLING C FUNCTIONS
Now we can get down to work calling C functions. To do this, we need to know three things: the name of the C function, its type, and its associated header file. Additionally, for code that is not provided by the standard C library, we will need to know the C library's name, for linking purposes. The actual binding work is done with a foreign import declaration.
foreign import ccall "math.h sin"
c_sin :: CDouble -> CDouble
This defines a new Haskell function, c_sin, whose concrete implementation is in C, via the sin function. When c_sin is called, a call to the actual sin will be made, using the standard C calling convention, indicated by the ccall keyword. The Haskell runtime passes control to C, which returns its results back to Haskell. The result is then wrapped up as a Haskell value of type CDouble. A common idiom when writing FFI bindings is to have the corresponding C function have a "c_" prefix as shown by c_sin above.
When writing the binding, the programmer has to translate C type signatures like this into their Haskell FFI equivalents, making sure that the data representations match up. For example, double in C corresponds to CDouble in Haskell.
D. WRAPPING BINDINGS
With the foreign imports out of the way, the next step is to convert the C types we pass to and receive from the foreign language call into native Haskell types, wrapping the binding so it appears as a normal Haskell function.
fastsin :: Double -> Double
fastsin x = realToFrac (c_sin (realToFrac x))
The main thing to remember when writing convenient wrappers over bindings like this is to convert input and output back to normal Haskell types correctly. To convert between floating point values, we can use realToFrac, which lets us translate different floating point values to each other and these conversions, such as from CDouble to Double, are usually free, as the underlying representations are unchanged.
E. MAIN
We can now proceed to use the bound function in a program. For example, we can apply the C sin function to a Haskell list of tenths.
main = mapM_ (print . fastsin) [0/10, 1/10 .. 10/10]
F. EXAMPLE C PROGRAM
Save the following complete file CboundFFI.hs to your working directory:
import Foreign
import Foreign.C.Types
foreign import ccall "math.h sin"
c_sin :: CDouble -> CDouble -- C function has a "c_" prefix.
fastsin :: Double -> Double
fastsin x = realToFrac (c_sin (realToFrac x)) -- Converts between floating point values.
main = mapM_ (print . fastsin) [0/10, 1/10 .. 10/10]
G. LOAD HASKELL EXAMPLE C PROGRAM
Load the above file CboundFFI.hs into GHCi as shown below:
[1 of 1] Compiling Main ( CboundFFI.hs, interpreted )
Ok, modules loaded: Main.
Prelude>
H. RUN HASKELL GHCI INTERPRETER MAIN PROGRAM
Prelude> main
0.0
9.983341664682815e-2
0.19866933079506122
0.2955202066613396
0.3894183423086506
0.4794255386042031
0.5646424733950355
0.6442176872376911
0.7173560908995228
0.7833269096274834
0.8414709848078965
Prelude>
I. COMPILE HASKELL EXAMPLE C PROGRAM
Prelude> :! ghc -O --make CboundFFI.hs
[1 of 1] Compiling Main ( CboundFFI.hs, CboundFFI.o )
Linking CboundFFI.exe ...
Prelude>
J. EXECUTE COMPILED HASKELL EXAMPLE C PROGRAM
Open your "Windows Command Prompt." In Windows this will be a window with the title "Command Prompt." This window has a black background and displays white letters and when you open it, it will display something similar to the following:
C:\Users\Tinnel>
After the > prompt finish typing in your working directory path and then the CboundFFI.exe file name as shown below, and press Enter:
C:\Users\Tinnel>Haskell2024\CboundFFI.exe
0.0
9.983341664682815e-2
0.19866933079506122
0.2955202066613396
0.3894183423086506
0.4794255386042031
0.5646424733950355
0.6442176872376911
0.7173560908995228
0.7833269096274834
0.8414709848078965
C:\Users\Tinnel>
As shown above, your compiled Haskell program was executed and displayed on your "Windows Command Prompt" window.
C:\Users\Tinnel>
After the > prompt finish typing in your working directory path and then the CboundFFI.exe file name as shown below, and press Enter:
C:\Users\Tinnel>Haskell2024\CboundFFI.exe
0.0
9.983341664682815e-2
0.19866933079506122
0.2955202066613396
0.3894183423086506
0.4794255386042031
0.5646424733950355
0.6442176872376911
0.7173560908995228
0.7833269096274834
0.8414709848078965
C:\Users\Tinnel>
As shown above, your compiled Haskell program was executed and displayed on your "Windows Command Prompt" window.
III. CONCLUSION
In this tutorial, you have received an introduction to Haskell Foreign Function Interface (FFI).
IV. REFERENCES
Bird, R. (2015). Thinking Functionally with Haskell. Cambridge, England: Cambridge University Press.
Davie, A. (1992). Introduction to Functional Programming Systems Using Haskell. Cambridge, England: Cambridge University Press.
Goerzen, J. & O'Sullivan, B. & Stewart, D. (2008). Real World Haskell. Sebastopol, CA: O'Reilly Media, Inc.
Hutton, G. (2007). Programming in Haskell. New York: Cambridge University Press.
Lipovača, M. (2011). Learn You a Haskell for Great Good!: A Beginner's Guide. San Francisco, CA: No Starch Press, Inc.
Thompson, S. (2011). The Craft of Functional Programming. Edinburgh Gate, Harlow, England: Pearson Education Limited.