Composing a Storable by its parts

haskell

Solution

This error is happening because the `:: typeN` annotations in the definition of `sizeOf` don't refer to the types of the same name in the instance declaration. This is just how Haskell works - type variables are only scoped over the type signature in which they appear.

An extension called `ScopedTypeVariables` will change that. Among other things, it makes type variables used in an instance declaration scope over all the definitions in the declaration.

Just insert the following line at the start of the file:

{-# LANGUAGE ScopedTypeVariables #-}

Alternatively, you could actually do something lazier than the pattern match. Since `FullData` is a record, the accessor functions restrict type appropriately.

sizeOf fd = sizeOf (first fd) + sizeOf (second fd)

As long as `sizeOf` is implemented correctly for the nested types, that is appropriately non-strict. Since the nested calls to `sizeOf` don't evaluate their arguments, the calls to `first` and `second` won't actually be evaluated - but their type will be calculated, and force things to work properly.

You could even use an irrefutable pattern match to do the same thing without using the field accessors:

sizeOf ~(FullData x y) = sizeOf x + sizeOf y

The `~` asserts to the compiler that the pattern will always match, and it can put off actually testing it until the values of `x` or `y` are needed. If you lied to the compiler about that pattern always matching, it will generate a runtime error when `x` or `y` is used, as it attempts to look them up but finds that the pattern wasn't actually matched properly. This works in a similar way to the previous case - if x and y aren't used, everything is good.

Problem

I want to compose a Foreign.Storable into two parts.... ``` import Foreign data FullData type1 type2 = FullData {first::type1, second::type2} instance (Storable type1, Storable type2)=>Storable (FullData type1 type2) where sizeOf _ = sizeOf (undefined::type1) + sizeOf (undefined::type2) alignment _ = 0 --I am just setting this to zero for testing.... main = putStrLn $ show $ sizeOf (undefined::FullData Int Char) ``` Yet this fails with- ``` storableTest.hs:13:44: Could not deduce (Storable a1) arising from a use of `sizeOf' from the context (Storable type1, Storable type2) bound by the instance declaration at storableTest.hs:12:10-74 The type variable `a1' is ambiguous Possible fix: add a type signature that fixes these type variable(s) Note: there are several potential instances: instance (Storable type1, Storable type2) => Storable (FullData type1 type2) -- Defined at storableTest.hs:12:10 instance Storable Bool -- Defined in `Foreign.Storable' instance Storable Char -- Defined in `Foreign.Storable' ...plus 16 others In the second argument of `(+)', namely `sizeOf (undefined :: type2)' In the expression: sizeOf (undefined :: type1) + sizeOf (undefined :: type2) In an equation for `sizeOf': sizeOf _ = sizeOf (undefined :: type1) + sizeOf (undefined :: type2) ``` Here are some more important facts- The same error appears even if I use ExistentialQuantification or RankNTypes and declare (Storable type1, Storable type2) directly in the data definition (outside the constructor or in the fields themselves). I can get this to work if I change the sizeOf definition to sizeOf (FullData x y) = sizeOf x + sizeOf y but this only works if I have a concrete instance of FullData, and some of my programs need to know the size of the data before an instance is created (I know, I could just have a dummy instance, but that seems kinda ugly).

Original source