haskellghcgadtderived-instances

How can I get GHC to generate instances of Data.Typeable for GADTs with Typeable in the context?


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.


Solution

  • I was hoping GHC would be able to pull the Typeable constraint from the context on the Wrap 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.