【问题标题】:Can't write a Show instance for this type无法为此类型编写 Show 实例
【发布时间】:2018-12-19 02:49:28
【问题描述】:

考虑以下类型声明:

data Foobar x = Foo (x (Foobar x))

现在为此编写一个Show 实例。我敢!

我随便写了deriving Show,期待GHC 能做到。但它抱怨说它做不到。 “可怜的傻瓜”,我想,然后自己来定义它。但是...嗯...哦,实际上这很有趣...显然Foobar x 仅在x y 可显示为可显示y 时才显示...或...类似的东西...啊啊!

现在我确定我不可能是第一个定义这种类型的人。我不能成为第一个想要Show 实例的人。所以一定有人想出了如何做到这一点。

【问题讨论】:

    标签: haskell typeclass


    【解决方案1】:

    如果您只是指定明显的约束——并添加一些必要的语言编译指示——一切都会正常工作。要使 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
    

    【讨论】:

    • 这是递归方案中的how Show is defined for Fix
    • 啊,是的,这是有道理的——因为这 Fix,直到重命名!我应该马上注意到的:-)
    • 我确定这种类型已经在某个地方的库中,但由于某种原因我看不到它......
    【解决方案2】:

    以下是不带扩展名的方法:

    data Foobar x = Foo (x (Foobar x))
    
    class Show1 f where
        showsPrec1 :: Show a => Int -> f a -> ShowS
    
    instance Show1 f => Show (Foobar f) where
        showsPrec n (Foo v) = ("Foo " ++) . showsPrec1 n v -- doesn't handle n correctly because that's not the point of the question and would only distract from the actual important idea
    

    Show1 类(以及一些标准类型的实例)也可用于on Hackage。定义它的模块使用了一些扩展——但只是为了减少样板,而不是启用无聊的旧 Haskell2010 无法做的任何事情。

    【讨论】:

      【解决方案3】:

      好的,所以在玩代码时,我发现了一种方法:

      instance (Functor f, Show (f String)) => Show (Foobar f) where
        show = fold show
      
      fold :: Functor f => (f x -> x) -> Foobar f -> x
      fold f (Foo x) = f (fmap (fold f) x)
      

      不过有几个问题。

      • 它不会产生完全正确的输出。 (Foo 构造函数丢失,子表达式被引用。)
      • 显然需要UndecidableInstances 才能工作。 (!)
      • 我尝试将 show 更改为包含 Foo 构造函数,但随后 GHC 出现恐慌 (!!!)

      我不知道您实际上可以通过编写用户代码来使 GHC 崩溃...显然您可以做到这一点。

      ghc: panic! (the 'impossible' happened)
        (GHC version 7.4.2 for i386-unknown-mingw32):
          kindFunResult ghc-prim:GHC.Prim.*{(w) tc 34d}
      
      Please report this as a GHC bug:  http://www.haskell.org/ghc/reportabug
      

      我猜它被称为“不可判定”是有原因的! o_O

      【讨论】:

      • 这种恐慌与UndecidableInstances 的(正确行为)无关——UndecidableInstances 可以做的最糟糕的事情(或者至少应该可以做!)是让类型检查器循环.我想说将其报告为错误,但您显然使用的是相当旧的 GHC 版本。如果您对其进行编辑以包含出现恐慌的代码,我可以在更新的 GHC 上对其进行测试。
      • 好的,谢谢。好消息:我无法在 Mac OS X 10.10.5 上的 GHC 7.10.2 下重现您的代码恐慌。
      • fold show 更改为 fold (\ x -&gt; "Foo (" ++ show x ++ ")")。它恐慌 7.4.2。我在 7.6.x 上尝试过它并没有惊慌,所以我很确定这个错误是什么,它早就被修复了。
      • 这真是奇怪的 o_O。但至少它是固定的! (在 7.10.2 确认。)
      猜你喜欢
      • 2018-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多