如果您只是指定明显的约束——并添加一些必要的语言编译指示——一切都会正常工作。要使 Foobar 可显示,Foo 的字段必须是可显示的,因此我们需要 Show (x (Foobar x)) 约束。如果我们要求这样做,一切都会顺利进行:
{-# LANGUAGE FlexibleContexts, UndecidableInstances, StandaloneDeriving #-}
data Foobar x = Foo (x (Foobar x))
deriving instance Show (x (Foobar x)) => Show (Foobar x)
然后:
λ> 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")])])
我有点惊讶这能奏效,但也不过分 :-)
顺便说一句,如果您尝试将 Show 派生为 Foobar,这正是 GHC 对您的要求:
λ> 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))
我们所要做的就是使用StandaloneDeriving 并准确指定该约束!
另外,需要明确的是,StandaloneDeriving 部分是可选的——它没有做任何魔法,而且手动编写实例并不难:
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