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