6

我想写一些类似的东西:

{-# LANGUAGE FlexibleContexts,FlexibleInstances #-}

import Data.ByteString.Char8 (ByteString,pack)
import Data.Foldable (Foldable)

class (Show a) => Rows a where
    rRepr :: a -> [ByteString]
    rRepr = (:[]) . pack . show

instance (Foldable f,Show (f a)) => Rows (f a) where
    rRepr = const []

意思是f a实例化Rows如果f实例化Foldablef a实例化Show。当我运行 ghc 我得到:

Constraint is no smaller than the instance head
  in the constraint: Show (f a)
(Use -XUndecidableInstances to permit this)
In the instance declaration for `Rows (f a)'

我有两个问题:

  • 错误中的“较小”是什么意思,问题是什么?
  • 在不使用的情况下定义我想要的东西的正确方法是什么UndecidableInstances
4

1 回答 1

11

让我们玩一下编译器:我们有一个类型(f a),我们想看看它是否是Rows约束的有效满足。为此,我们需要发送一个也(f a)满足的证明Show (f a)。这不是问题,除非有人写

 instance Rows (f a) => Show (f a) where ...

在这种情况下,我又回到了我开始的地方。像这样编码一个无限循环有点愚蠢,但是 Haskell 静态地确保它实际上是不可能的,除非你要求UndecidableInstances.


通常,Haskell 确保“解析追逐”的每一步都会将类型的大小减少至少 1 个构造函数。这通过结构归纳得出了一个非常简单的证明,即我们最终会得到一个具有有限数量分辨率的裸类型。

这是过于严格的,显然一些实例解析步骤是有意义的、有用的和完整的,即使它们不会立即减少构造函数树。这种整体性限制适用于像 Agda 和 Coq 这样的语言,并且通常将您的算法操纵成一个通过结构归纳进行的算法是一个说明性的挑战。


那么我们该如何解决呢?一种方法是失去Show对定义的约束,使用prelude-extras之类的class实例。Show1

class Show1 f where ...
show1 :: (Show1 f, Show a) => f a -> String  -- not an instance definition!

然后instance (Foldable f, Show1 f, Show a) => Rows (f a) where ...在我的测试中使用哪个。然后,您可以将默认实例编写为独立函数。

defRRepr :: Show a => a -> [ByteString]
defRRepr = (:[]) . pack . show

Show并在为有能力的事物编写实例定义时使用它。


另一种解决方案是使用newtype包装器来允许 Haskell 看到已删除了“层”解析。

instance (Foldable f, Show (f a)) => Rows (FoldableRow f a) where
    rRepr = const [] . unFR

newtype FoldableRow f a = FR { unFR :: f a } deriving (Show)
于 2013-07-25T19:22:44.970 回答