Slide 1

Slide 1 text

Haskell FFI Basics Fraser Tweedale @hackuador March 24, 2015

Slide 2

Slide 2 text

Intro

Slide 3

Slide 3 text

This talk What’s an FFI? Haskell C FFI c2hs Other tools and resources

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

C - important aspects Header files (*.h) Linking to library object code Pointers Oh my God, it’s full of stars!

Slide 6

Slide 6 text

Haskell FFI

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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);

Slide 10

Slide 10 text

Haskell FFI - pointers #include 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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

Haskell FFI - Storable class Storable a where sizeOf :: a -> Int alignment :: a -> Int peek :: Ptr a -> IO a poke :: Ptr a -> a -> IO ()

Slide 16

Slide 16 text

c2hs

Slide 17

Slide 17 text

c2hs Preprocessor to simplify bindings Enums and typedefs Automatic foreign import (demand-based) *.chs file extensions; outputs *.hs

Slide 18

Slide 18 text

Detour: notmuch Mail indexer Written in C++ (exports C interface) I have written a binding (immature)

Slide 19

Slide 19 text

c2hs - enum (C) typedef enum { NOTMUCH_SORT_OLDEST_FIRST, NOTMUCH_SORT_NEWEST_FIRST, NOTMUCH_SORT_MESSAGE_ID, NOTMUCH_SORT_UNSORTED } notmuch_sort_t;

Slide 20

Slide 20 text

c2hs - enum (Haskell) {#enum notmuch_sort_t as Sort {underscoreToCase} #} -- TURNS INTO -- data Sort = NotmuchSortOldestFirst | NotmuchSortNewestFirst | NotmuchSortMessageId | NotmuchSortUnsorted deriving (Enum)

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

c2hs - calls (C) const char * notmuch_message_get_message_id( notmuch_message_t *message );

Slide 23

Slide 23 text

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)))

Slide 24

Slide 24 text

c2hs {#context prefix = "notmuch" #} Finalisers must be manually attached (use addForeignPtrFinalizer) pointer directive without newtype makes type synonym

Slide 25

Slide 25 text

Other tools

Slide 26

Slide 26 text

cabal build-tools: c2hs >= 0.15 extra-libraries: notmuch

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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/

Slide 29

Slide 29 text

Conclusion

Slide 30

Slide 30 text

What we covered FFI - what and why? Direct C FFI usage c2hs examples Overview of other tools You can write a binding now!

Slide 31

Slide 31 text

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)

Slide 32

Slide 32 text

Resources Haskell 2010 Report, FFI chapter: http://is.gd/qF20e9 https://en.wikibooks.org/wiki/Haskell/FFI https://wiki.haskell.org/FFI_cook_book c2hs documentation: http://is.gd/JpX0Ku https://github.com/frasertweedale/hs-notmuch

Slide 33

Slide 33 text

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