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).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With