Say we have the following code, where a couple of types end up being place inside two other types, the outer-most of which is a GADT:
{-# LANGUAGE FlexibleInstances,
GADTSyntax,
GADTs,
OverlappingInstances,
StandaloneDeriving #-}
data SomeType1 = SomeType1 deriving Show
data SomeType2 = SomeType2 deriving Show
class SomeClass d where
instance SomeClass SomeType1 where
instance SomeClass SomeType2 where
data WrapperType t where
WrapperType :: (SomeClass t, Show t) => t -> (WrapperType t)
instance Show (WrapperType SomeType1) where
show (WrapperType d) = "correct"
instance Show (WrapperType t) where
show (WrapperType d) = "incorrect"
data ListWrap where
ListWrap :: [(WrapperType d)] -> ListWrap
deriving instance Show ListWrap
Now, writing [WrapperType SomeType1] gives me what I want:
*MyModule> [WrapperType SomeType1]
[correct]
But as soon as I put it inside ListWrap I get the wrong Show instance picked to display the contents:
*MyModule> ListWrap [WrapperType SomeType1]
ListWrap [incorrect]
There must be something about type classes and/or GADTs that I'm failing to understand -- what could it be?
If you look at the derived code (with -ddump-deriv) then you can see that the derived instance for ListWrap is pretty normal looking
instance Show ListWrap where
showsPrec a (ListWrap b) =
showParen (a >= 11) $ showString "ListWrap " . showsPrec 11 b
it's just passing the inner show down to showsPrec again. This suggests that the problem is that GHC is erasing the type variable d when you wrap it with ListWrap. Indeed, you could also write ListWrap as
data ListWrap where
ListWrap :: forall d. [(WrapperType d)] -> ListWrap
which emphasizes that this is existentially typed.
We can see the error more directly by deleting the instance Show (WrapperType t) and observing GHC's type error
/Users/tel/tmp/foo.hs:33:52: Warning:
No instance for (Show (WrapperType d))
arising from a use of `showsPrec'
Possible fix:
add an instance declaration for (Show (WrapperType d))
In the second argument of `(.)', namely `showsPrec 11 b'
In the second argument of `($)', namely
`showString "ListWrap " . showsPrec 11 b'
In the expression:
showParen (a >= 11) $ showString "ListWrap " . showsPrec 11 b
Ok, modules loaded: Main.
In other words, it has lost the details about the d type and thus cannot unify the specific instance Show (WrapperType SomeType1).
Now you would think that this would imply that keeping that type information around would make the type error go away.
data ListWrap d where
ListWrap :: [(WrapperType d)] -> ListWrap d
> show $ ListWrap [WrapperType SomeType1]
"ListWrap [incorrect]"
But it seems like the instance search is going awry as well. The only way I could find to make it work was to turn on UndecidableInstances and provide a suggestion for instance derivation.
deriving instance Show (WrapperType d) => Show (ListWrap d)
after which the example works
> show $ ListWrap [WrapperType SomeType1]
"ListWrap [correct]"
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