Skip to content

Haskell FFI Guide — Foreign Function Interface and C Interoperability

DodaTech Updated 2026-06-28 2 min read

In this tutorial, you will learn about Haskell FFI Guide. We cover key concepts, practical examples, and best practices to help you master this topic.

Haskell FFI enables calling C functions directly using foreign import declarations, with Foreign.Storable for marshalling Haskell values to C memory, ForeignPtr for garbage-collected foreign memory, and alloca for stack allocation.

Basic FFI

import Foreign.C.Types

-- Import C function
foreign import ccall "math.h sin"
    c_sin :: CDouble -> CDouble

-- Pure wrapper
sin' :: Double -> Double
sin' x = realToFrac (c_sin (realToFrac x))

-- Import with side effects
foreign import ccall "stdio.h puts"
    c_puts :: CString -> IO CInt

Memory Allocation

import Foreign
import Foreign.C.Types

-- Stack allocation with alloca
withInt :: (Ptr CInt -> IO a) -> IO a
withInt = alloca

useInt = withInt $ \ptr -> do
    poke ptr 42
    val <- peek ptr
    print (val :: CInt)

-- Heap allocation
mallocInt :: IO (Ptr CInt)
mallocInt = malloc

freeInt :: Ptr CInt -> IO ()
freeInt = free

Structured Data

import Foreign.Storable

data Point = Point { px :: CDouble, py :: CDouble }

instance Storable Point where
    sizeOf _ = 16  -- two doubles
    alignment _ = 8
    
    peek ptr = Point
        <$> peekByteOff ptr 0
        <*> peekByteOff ptr 8
    
    poke ptr (Point x y) = do
        pokeByteOff ptr 0 x
        pokeByteOff ptr 8 y

ForeignPtr

import Foreign.ForeignPtr

-- Automatically managed foreign memory
mkForeignPtr :: IO (ForeignPtr CInt)
mkForeignPtr = do
    ptr <- malloc
    newForeignPtr finalizerFree ptr
  where
    finalizerFree p = free p

-- Using withForeignPtr
useForeign :: ForeignPtr CInt -> IO CInt
useForeign fptr = withForeignPtr fptr peek

Common Mistakes

1. Memory leaks

Forgetting to free allocated memory. Use ForeignPtr for automatic GC-managed cleanup.

2. Wrong type sizes

C types have platform-dependent sizes. Use CInt, CLong, CDouble etc. from Foreign.C.Types for portability.

3. Dangling pointers

Don't use a Ptr after free. ForeignPtr protects against this, but manual management requires care.

Practice Questions

1. How do you import a C function? foreign import ccall "header.h func_name" haskell_name :: Type.

2. What does alloca do? Allocates memory on the C stack for the duration of a computation. Automatically freed.

3. What is the difference between Ptr and ForeignPtr? Ptr is raw and requires manual management. ForeignPtr is garbage-collected with a finalizer.

FAQ

{{< faq question="Can I call Haskell from C?" >}} Yes. Use foreign export ccall to make Haskell functions callable from C. Compile with -fno-warn-foreign-safe. {{< /faq >}}

{{< faq question="How do I handle strings?" >}} Use CString from Foreign.C.String. Functions: newCString, peekCString, withCString. {{< /faq >}}

{{< faq question="What is the stable pointer?" >}} Foreign.StablePtr creates a stable reference to a Haskell value that can be passed to C and converted back. {{< /faq >}}

What's Next

Now learn about profiling.

Topic Description Link
Profiling Performance profiling {{< ref "23-profiling" >}}
Documentation Haddock documentation {{< ref "24-documentation" >}}

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro