2
votes

I have the following data type in a Haskell module and I would like to write a Storable instace to use it with C via FFI :

data MyType a =
        TypeDouble Double
      | TypeLst [a] 
      | TypeAdd (MyType a) (MyType a) 

I began by defining the sizeOf function :

instance Storable a => Storable (MyType a)  where
  sizeOf (TypeDouble _) = sizeOf (0 :: Double)
  sizeOf (TypeLst lst)  = sum $ map sizeOf lst
  sizeOf (TypeAdd a b)  = sizeOf a + sizeOf b

It compile well, but I don't know how to implement the peek and poke functions. I thought implementing these functions the same way as in this answer but this implementation work only if all the elements in the list have the same size which is not the case here.

What is the correct way to implement peek and poke functions for a recursive type where elements have a floating size ?

1
Maybe you should first define the C type you are expecting to use as the counterpart of your Haskell MyType a. Your sizeOf seems to be a bit optimistic to me. Your C type will probably need an int/enum/whatever tag that helps you discriminate between the three Haskell constructors; the size of that tag should be added to all the sizes.chi
It's worth noting that sizeOf has, for historical reasons, the wrong signature. It should actually be Tagged a Int (or proxy a -> Int) rather than a -> Int. That would make it clear that this can't actually depend on individual values, but should be the same for all value of a given type. (The documentation does say this, though.)leftaroundabout
@leftaroundabout even that signature is now historical. With TypeApplications and AllowAmbigousTypes it should really be sizeOf :: Storable a => Int, usable via sizeOf @a.HTNW
@HTNW well, yeah, but that's a very recent kind of “historical”. And it can't be made backwards-compatible with old GHC, let alone other Haskell implementations, so this isn't very practical yet for such a low-level feature.leftaroundabout

1 Answers

7
votes

You can’t have a Storable for this. These datatypes need to have a fixed size, just like C structs. Also, note that sizeof isn’t supposed to inspect the value you give it. It’s just a proxy/carrier for the type argument, so you can write e.g. sizeof (undefined::Int). Maybe take a look at Foreign.Marshal.Array