Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 335
yatima2975 Avatar asked Mar 20 '13 18:03

yatima2975


1 Answers

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.

like image 163
Daniel Fischer Avatar answered Sep 22 '22 12:09

Daniel Fischer