Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Haskell FFI Basics

Haskell FFI Basics

Wanting to make use of libraries written in other langauges (often C) is a common situation in high-level languages, and Haskell is no exception. This talk will introduce and demonstrate real-world use of Haskell's C foreign function interface (FFI), including how to deal with some common C idioms. We will examine the c2hs tool which simplifies bindings and review some other Haskell FFI tools.

Attendees who are comfortable with Haskell syntax and have a passing familiarity with C (especially pointers) will get the most out of this presentation.

Fraser Tweedale

March 24, 2015
Tweet

More Decks by Fraser Tweedale

Other Decks in Programming

Transcript

  1. What is an FFI? Mechanism to call code written in

    one language from code written in another language Often used to bind or wrap an entire library (usually C) Sometimes used to export routines to be called from other language
  2. C - important aspects Header files (*.h) Linking to library

    object code Pointers Oh my God, it’s full of stars!
  3. Haskell FFI {-# LANGUAGE ForeignFunctionInterface #-} import Foreign import Foreign.C.Types

    foreign import ccall unsafe "math.h sin" c_sin :: CDouble -> CDouble foreign import ccall unsafe "stdlib.h rand" c_rand :: IO CUInt
  4. Haskell FFI - safe and unsafe safe is required when

    foreign function can call back into Haskell runtime safe is recommended when foreign function could block safe has performance penalties safe is the default
  5. Haskell FFI - pointers int thing_get_version(thing_t *thing); char *thing_get_name(thing_t *thing);

    int thing_new(char *name, thing_t **p); void thing_free(thing_t *thing);
  6. Haskell FFI - pointers #include <thing.h> foreign import ccall "thing_get_version"

    thing_get_version :: Ptr Thing -> CUInt foreign import ccall "thing_get_name" thing_get_name :: Ptr Thing -> Ptr CChar foreign import ccall "thing_new" thing_new :: Ptr CChar -> Ptr (Ptr Thing) -> IO CUInt
  7. Haskell FFI - pointers withCString :: String -> (CString ->

    IO a) -> IO a peekCString :: CString -> IO String peek :: Ptr a -> IO a alloca :: Storable a => (Ptr a -> IO b) -> IO b
  8. Haskell FFI - pointers newtype Thing = Thing (Ptr Thing)

    makeThing :: String -> IO (Maybe Thing) makeThing s = withCString s $ \name -> alloca $ \ptr -> do result <- thing_new name ptr if result == 0 then Just . Thing <$> peek ptr else return Nothing
  9. Haskell FFI - garbage collection type FinalizerPtr a = FunPtr

    (Ptr a -> IO ()) newForeignPtr :: FinalizerPtr a -> Ptr a -> IO (ForeignPtr a) addForeignPtrFinalizer :: FinalizerPtr a -> ForeignPtr a -> IO () withForeignPtr :: ForeignPtr a -> (Ptr a -> IO b) -> IO b
  10. Haskell FFI - garbage collection foreign import ccall "&thing_free" thing_free

    :: FinalizerPtr a newtype Thing = Thing (FinalizerPtr Thing) makeThing :: String -> IO (Maybe Thing) makeThing s = withCString s $ \name -> alloca $ \ptr -> do result <- thing_new name ptr if result == 0 then Just . Thing . newForeignPtr thing_free <$> peek ptr else return Nothing
  11. Haskell FFI - Storable class Storable a where sizeOf ::

    a -> Int alignment :: a -> Int peek :: Ptr a -> IO a poke :: Ptr a -> a -> IO ()
  12. c2hs Preprocessor to simplify bindings Enums and typedefs Automatic foreign

    import (demand-based) *.chs file extensions; outputs *.hs
  13. c2hs - enum (Haskell) {#enum notmuch_sort_t as Sort {underscoreToCase} #}

    -- TURNS INTO -- data Sort = NotmuchSortOldestFirst | NotmuchSortNewestFirst | NotmuchSortMessageId | NotmuchSortUnsorted deriving (Enum)
  14. c2hs - typedef /* C */ typedef struct _notmuch_message notmuch_message_t;

    -- HASKELL -- {#pointer *notmuch_message_t as Message foreign newtype #} -- TURNS INTO -- newtype Message = Message (ForeignPtr (Message)) withMessage :: Message -> (Ptr Message -> IO b) -> IO b withMessage (Message fptr) = withForeignPtr fptr
  15. c2hs - calls (Haskell) message_get_message_id :: Message -> IO String

    message_get_message_id ptr = withMessage ptr ( {#call notmuch_message_get_message_id #} >=> peekCString ) -- TURNS INTO -- message_get_message_id :: Message -> IO String message_get_message_id ptr = withMessage ptr (notmuch_message_get_message_id >=> peekCString) foreign import ccall safe "notmuch_message_get_message_id" notmuch_message_get_message_id :: ((Ptr (Message)) -> (IO (Ptr CChar)))
  16. c2hs {#context prefix = "notmuch" #} Finalisers must be manually

    attached (use addForeignPtrFinalizer) pointer directive without newtype makes type synonym
  17. hsc2hs *.hsc (compare *.chs) Can bind to #define, e.g. macros,

    constants Better Storable boilerplate automation Otherwise, c2hs is more powerful Discussion of differences (Stack Overflow): http://is.gd/weyuYN
  18. More tools c2hsc .h -> .hcs, .hsc.helper.c bindings-DSL CPP macros

    to help write bindings language-c-inline Inline C and Objective C via TH https: //github.com/mchakravarty/language-c-inline/
  19. What we covered FFI - what and why? Direct C

    FFI usage c2hs examples Overview of other tools You can write a binding now!
  20. What we didn’t cover Calling Haskell from C (foreign export)

    Dealing with external GCs, “special” allocators Storable typeclass hsc2hs or other tools in any detail Other Haskell FFIs (e.g. JavaScriptFFI)
  21. Fin Copyright 2015 Fraser Tweedale This work is licensed under

    the Creative Commons Attribution 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/. Slides https://github.com/frasertweedale/talks/ Email [email protected] Twitter @hackuador