Problem: Going from Haskell types to Foreign types and back requires a lot of boilerplate code.
For example, suppose we're working with the following Haskell data structure:
data HS_DataStructure = HS_DataStructure {
a1 :: String
, b1 :: String
, c1 :: Int
}
In order to get this data structure into the land of C, we will need to consider its struct
analogue:
typedef struct {
char *a;
char *b;
int c;
} c_struct;
But in order to pass such a struct into C from Haskell, we have to transform HS_DataStructure
into the following:
data HS_Struct = HS_Struct {
a :: CString
, b :: CString
, c :: CInt
} deriving Show
We then have to make HS_Struct
an instance of Storable
:
instance Storable HS_Struct where
sizeOf _ = #{size c_struct}
alignment _ = alignment (undefined :: CString)
poke p c_struct = do
#{poke c_struct, a} p $ a c_struct
#{poke c_struct, b} p $ b c_struct
#{poke c_struct, c} p $ c c_struct
peek p = return HS_Struct
`ap` (#{peek c_struct, a} p)
`ap` (#{peek c_struct, b} p)
`ap` (#{peek c_struct, c} p)
(In the above I'm using hs2c syntax).
Now finally, in order to convert between HS_Struct
and HS_DataStructure
, we are forced to use the following helper functions(!):
makeStruct :: HS_DataStructure -> IO (HS_Struct)
makeStruct hsds = do str1 <- newCString (a1 hsds)
str2 <- newCString (b1 hsds)
jreturn (HS_Struct str1 str2 (c1 hsds))
makeDataStructure :: Ptr (HS_Struct) -> IO (HS_DataStructure)
makeDataStructure p = do hss <- peek p
hs1 <- peekCString (a hss)j
hs2 <- peekCString (b hss)
return (HS_DataStructure hs1 hs2 (c hss))
This seems to be an insane amount of boilerplate to go back and forth between Haskell and C.
Questions
- Is there any way to minimize the boilerplate above?
- With Haskell projects that involve a heavy amount of FFI, is it idiomatic to just give in and primarily use Haskell's C types (i.e.,
CInt
,CString
, and so forth)? This would at least save you the hastle having to convert back and forth between the types.