Write Haskell source files including C code inline. No FFI required.
inline-c
inline-c
lets you seamlessly call C libraries and embed high-performance inline C code in Haskell modules. Haskell and C can be freely intermixed in the same source file, and data passed to and from code in either language with minimal overhead. No FFI required.
inline-c
is Haskell's escape hatch (or one of) to the wild world of legacy code and high-performance numerical and system libraries. It has other uses too: you can also think of inline-c
as to Haskell what inline Assembly is to C — a convenient means to eke out a little bit of extra performance in those rare cases where C still beats Haskell.
GHCi support is currently limited to using -fobject-code
, see the last section for more info.
Getting started
Let's say we want to compute the cosine of a number using C from Haskell. inline-c
lets you write this function call inline, without any need for a binding to the foreign function:
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
import qualified Language.C.Inline as C
C.include "<math.h>"
main :: IO ()
main = do
x <- [C.exp| double{ cos(1) } |]
print x
inline-c
leverages the quasiquotation language extension implemented in GHC. Template Haskell is also required. Importing the Language.C.Inline
module brings in scope most required Haskell definitions. C.include "<math.h>"
brings into scope the foreign function cos()
that we wish to call. Finally, in the main
function, [C.exp| double { cos(1) } |]
denotes an inline C expression of type double
. cexp
stands for "C expression". It is a custom quasiquoter provided by inline-c
.
A C.exp
quasiquotation always includes a type annotation for the inline C expression. This annotation determines the type of the quasiquotation in Haskell. Out of the box, inline-c
knows how to map many common C types to Haskell types. In this case,
[C.exp| double { cos(1) } |] :: IO CDouble
For pure C expression like these we also provide C.pure
, which works exactly the same but without the IO
:
[C.pure| double { cos(1) } |] :: CDouble
Obviously extra care must be taken when using C.pure
: the embedded C code must be referentially transparent.
Multiple statements
inline-c
allows embedding arbitrary C code, not just expressions, in the form of a sequence of statements, using the c
quasiquoter:
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
import qualified Language.C.Inline as C
C.include "<stdio.h>"
main :: IO ()
main = do
x <- [C.block| int {
// Read and sum 5 integers
int i, sum = 0, tmp;
for (i = 0; i < 5; i++) {
scanf("%d", &tmp);
sum += tmp;
}
return sum;
} |]
print x
Just as with C.exp
, we need a type annotation on the entire C block. The annotation specifies the return type. That is, the type of the expression in any return statement.
Capturing Haskell variables -- parameter declaration
inline-c
allows referring to Haskell variables inside C expressions and code blocks. We do so by "anti-quoting" them.
Let's say that we wanted to parameterize the function we wrote above by how many numbers we should read. We can do so by defining a Haskell function whose parameter we can refer to from within C:
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
import qualified Language.C.Inline as C
import Foreign.C.Types
C.include "<stdio.h>"
-- | @readAndSum n@ reads @n@ numbers from standard input and returns
-- their sum.
readAndSum :: CInt -> IO CInt
readAndSum n = [C.block| int {
// Read and sum n integers
int i, sum = 0, tmp;
for (i = 0; i < $(int n); i++) {
scanf("%d", &tmp);
sum += tmp;
}
return sum;
} |]
main :: IO ()
main = do
x <- readAndSum 5
print x
Here, the Haskell variable n
is captured right where we need it using $(int n)
. Standard anti-quotation (we'll talk about additional ones later) consists of a $
followed by a C declaration in parenthesis. Note that any valid Haskell identifiers can be used when anti-quoting, including ones including constructors, qualified names, names containing unicode, etc.
For each anti-quotation, a variable with a matching type is expected in the Haskell environment. In this case inline-c
expects a variable named n
of type CInt
, which is the case.
What can be captured and returned?
All C types correspond to exactly one Haskell type. Basic types (int
, long
, double
, float
, etc.) get converted to their Haskell equivalents CInt
, CLong
, CDouble
, CFloat
. Pointers and arrays get converted to Ptr
. Function pointers get converted to FunPtr
.
inline-c
can also handle user-defined structs and enums, provided that they are instances of Storable
and that you tell inline-c
about them using contexts.
Contexts
Everything beyond the base functionality provided by inline-c
is specified in a structure that we call "Context
". From a user perspective, if we want to use anything but the default context (C.baseCtx
), we must set the C.Context
explicitly using the C.context
function. The next two sections include several examples.
The C.Context
allows to extend inline-c
to support
- Custom C types beyond the basic ones;
- And additional anti-quoters.
C.Context
s can be composed using their Monoid
instance.
Ideally a C.Context
will be provided for each C library that should be used with inline-c
. The user can then combine multiple contexts together if multiple libraries are to be used in the same program. See the inline-c-nag
package for an example of using a C.Context
tailored for a library.
For information regarding how to define C.Context
s, see the Haddock-generated API documentation for Language.C.Inline.Context
.
More anti-quoters
Besides the basic anti-quoter, which captures variables as they are, some more anti-quoters are provided with additional functionality. As mentioned, inline-c
can easily be extended with anti-quoters defined by the user, using contexts.
Vectors
The vec-len
and vec-ptr
anti-quoters in the C.vecCtx
context let us easily use Haskell vectors in C. Continuing along the "summing" theme, we can write code that sums Haskell vectors in C:
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
import qualified Language.C.Inline as C
import qualified Data.Vector.Storable as V
import qualified Data.Vector.Storable.Mutable as VM
import Data.Monoid ((<>))
import Foreign.C.Types
-- To use the vector anti-quoters, we need the 'C.vecCtx' along with the
-- 'C.baseCtx'.
C.context (C.baseCtx <> C.vecCtx)
sumVec :: VM.IOVector CDouble -> IO CDouble
sumVec vec = [C.block| double {
double sum = 0;
int i;
for (i = 0; i < $vec-len:vec; i++) {
sum += $vec-ptr:(double *vec)[i];
}
return sum;
} |]
main :: IO ()
main = do
x <- sumVec =<< V.thaw (V.fromList [1,2,3])
print x
The vec-len
anti-quoter is used simply by specifying the vector we want to get the length of (in our case, vec
). To use the vec-ptr
anti-quoter it is also required to specify the pointer type we want. Since vec
is a vector of CDouble
s, we want a pointer to double
s.
ByteStrings
The bs-len
and bs-ptr
anti-quoters in the C.bsCtx
context work exactly the same as the vec-len
and vec-ptr
counterparts, but with strict ByteString
s. The only difference is that it is not necessary to specify the type of the pointer from C -- it is always going to be char *
:
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
import qualified Data.ByteString as BS
import Data.Monoid ((<>))
import Foreign.C.Types
import qualified Language.C.Inline as C
C.context (C.baseCtx <> C.bsCtx)
-- | Count the number of set bits in a 'BS.ByteString'.
countSetBits :: BS.ByteString -> IO CInt
countSetBits bs = [C.block|
int {
int i, bits = 0;
for (i = 0; i < $bs-len:bs; i++) {
char ch = $bs-ptr:bs[i];
bits += (ch * 01001001001ULL & 042104210421ULL) % 017;
}
return bits;
}
|]
Function pointers
Using the fun
anti-quoter, present in the C.funCtx
context, we can easily turn Haskell function into function pointers.
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
import qualified Language.C.Inline as C
-- To use the function pointer anti-quoter, we need the 'C.funCtx' along with
-- the 'C.baseCtx'.
C.context (C.baseCtx <> C.funCtx)
ackermann :: CLong -> CLong -> CLong
ackermann m n
| m == 0 = n + 1
| m > 0 && n == 0 = ackermann (m - 1) 1
| otherwise = ackermann (m - 1) (ackermann m (n - 1))
main :: IO ()
main = do
let ackermannIO m n = return $ ackermann m n
let x = 3
let y = 4
z <- [C.exp| long{
$fun:(long (*ackermannIO)(long, long))($(long x), $(long y))
} |]
print z
In this example, we capture a Haskell function of type CLong -> CLong -> IO CLong
, ackermannIO
, to a function pointer in C, using the fun
anti-quoter. Note how we need to specify the function pointer type when we capture ackermannIO
, using standard C declaration syntax. Also note that the fun
anti-quoter works with IO
functions, and so we needed to modify ackermann
to make it have the right type.
In general, when anti-quoting, if the type can be inferred (like in the case of vec-len
), only the Haskell identifier appears. If it can't, the target C type and the Haskell identifier are mentioned using C declaration syntax.
GHCi
Currently inline-c
does not work in interpreted mode. However, GHCi can still be used using the -fobject-code
flag. For speed, we recommend passing -fobject-code -O0
, for example
stack ghci --ghci-options='-fobject-code -O0'
or
cabal repl --ghc-options='-fobject-code -O0'