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

1

7

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.

Gallimaufry answered 20/3, 2013 at 18:6 Comment(0)
O
5

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.

Oscillator answered 20/3, 2013 at 18:50 Comment(3)
Right, I keep forgetting about the finer points of undefined as I usually store something in my datatypes :-) I think I need some sort of newtype GADTs, then! And an additional question, if I may: is the unconstrained Typeable (Wrapper t) instance I wrote in any way bad, provided I don't care that typeOf (error "aargh" :: Wrapper Bool) bottoms out?Gallimaufry
Well, it's a slightly surprising instance since you can't use it with undefined :: Wrapper Bool like other Typeable instances. If you make that public, at least put a warning on it. But I don't think that should necessarily stop you. In future GHC releases, however - methinks from 7.8 onwards, all Typeable instances will be provided by GHC, and for all types, iirc. So you won't have to deal with such problems then.Oscillator
Looking at the repository, you'll need -XAutoDeriveTypeable. Cool! I think I might just go and clone 7.8.1 over the weekend for that, but the last time I built GHC was in 2002 (early 6.x cycle?), so that's a bit scary... But thanks again!Gallimaufry

© 2022 - 2024 — McMap. All rights reserved.