Consider the following type declaration:
data Foobar x = Foo (x (Foobar x))
Now write a Show
instance for that. I dare you!
I casually wrote deriving Show
, expecting GHC to do it's thing. But it complained that it couldn't do it. "Poor simpleton", I thought, and proceeded to define it myself. But... um... oh, actually that's quite interesting... Clearly Foobar x
is only showable when x y
is showable for showable y
... or... something like that... aaargh!
Now I'm sure I cannot possibly be the first person to define a type such as this. And I can't be the first person to want a Show
instance for it. So somebody must have figured out how to do this.
If you just specify the obvious constraint – and add some necessary language pragmas – everything works just fine. For Foobar
to be showable, the field of Foo
must be showable, so we require a Show (x (Foobar x))
constraint. And if we ask for that, everything just works out:
{-# LANGUAGE FlexibleContexts, UndecidableInstances, StandaloneDeriving #-}
data Foobar x = Foo (x (Foobar x))
deriving instance Show (x (Foobar x)) => Show (Foobar x)
Then:
λ> print $ Foo [Foo [], Foo [Foo []]]
Foo [Foo [],Foo [Foo []]]
λ> data SL a = S String | L [a] deriving Show
λ> print $ Foo (L [Foo (S "a"), Foo (L [Foo (S "b"), Foo (L []), Foo (S "c")])])
Foo (L [Foo (S "a"),Foo (L [Foo (S "b"),Foo (L []),Foo (S "c")])])
I'm a little bit surprised this works, but not overly so :-)
Incidentally, this is exactly what GHC asks of you if you try to derive Show
for Foobar
:
λ> data Foobar x = Foo (x (Foobar x)) deriving Show
<interactive>:2:45:
No instance for (Show (x (Foobar x)))
arising from the first field of ‘Foo’ (type ‘x (Foobar x)’)
Possible fix:
use a standalone 'deriving instance' declaration,
so you can specify the instance context yourself
When deriving the instance for (Show (Foobar x))
All we had to do was use StandaloneDeriving
and specify exactly that constraint!
Also, to be clear, the StandaloneDeriving
part is optional – it's not doing any magic, and the instance isn't too hard to write by hand:
instance Show (x (Foobar x)) => Show (Foobar x) where
showsPrec p (Foo x) = showParen (p > app_prec) $
showString "Foo " . showsPrec (app_prec + 1) x
where app_prec = 10 :: Int