Suppose I have the following code:
{-# LANGUAGE GADTs, DeriveDataTypeable, StandaloneDeriving #-}
import Data.Typeable
class Eq t => OnlyEq t
class (Eq t, Typeable t) => BothEqAndTypeable t
data Wrapper a where
Wrap :: BothEqAndTypeable a => a -> Wrapper a
deriving instance Eq (Wrapper a)
deriving instance Typeable1 Wrapper
Then, the following instance declaration works, without a constraint on t
:
instance OnlyEq (Wrapper t)
and does what I expect it to do.
But the following instance declaration doesn't work:
instance BothEqAndTypeable (Wrapper t)
since GHC - I'm using 7.6.1 - complains that:
No instance for (Typeable t)
arising from the superclasses of an instance declaration
Possible fix:
add (Typeable t) to the context of the instance declaration
In the instance declaration for `BothEqAndTypeable (Wrapper t)'
Adding Typeable t
to the context works, of course. But so does adding the following instance:
instance Typeable (Wrapper t) where
typeOf (Wrap x) = typeOf1 (Wrap x) `mkAppTy` typeOf x
Is there a way to get GHC to write this latter instance for me? If so, how? If not, why not?
I was hoping GHC would be able to pull the Typeable
constraint from the context on the Wrap
constructor, just as it did with the Eq
constraint.
I think that my problems boils down to the fact that GHC explicitly disallows writing deriving instance Typeable (Wrapper t)
, and the standard (Typeable1 s, Typeable a) => Typeable (s a)
instance can't 'look inside' s a
to find a Typeable a
dictionary.
I was hoping GHC would be able to pull the
Typeable
constraint from the context on theWrap
constructor
If it had a Wrap
constructor, it could pull the Typeable
constraint from it.
But it doesn't have a Wrap
constructor.
The difference is that the Eq
instance uses the value, so it's either a Wrap something
, where the Wrap
constructor makes the Eq
dictionary for the wrapped type available, and everything is fine, or it's ⊥
, and then everything is fine too, evaluating x == y
bottoms out.
Note that the derived
instance Eq (Wrapper a)
does not have an Eq
constraint on the type variable a
.
Prelude DerivT> (undefined :: Wrapper (Int -> Int)) == undefined
*** Exception: Prelude.undefined
Prelude DerivT> (undefined :: (Int -> Int)) == undefined
<interactive>:3:29:
No instance for (Eq (Int -> Int)) arising from a use of `=='
Possible fix: add an instance declaration for (Eq (Int -> Int))
In the expression: (undefined :: Int -> Int) == undefined
In an equation for `it':
it = (undefined :: Int -> Int) == undefined
But the Typeable
instance must not make use of the value, so there's no bottoming out if the supplied value isn't a Wrap something
.
Thus the derived instance Typeable1 Wrapper
supplies
instance Typeable t => Typeable (Wrapper t)
but not an unconstrained
instance Typeable (Wrapper t)
and that unconstrained instance cannot be derived by GHC.
Hence you have to either provide a constrained
instance Typeable t => BothEqAndTypeable (Wrapper t)
or an unconstrained
instance Typeable (Wrapper t)
yourself.