4

我被这个重叠的实例错误消息难住了。抱歉,这是一个不平凡的项目,但错误应该是类型签名的本地错误。

首先,我声明f是某种类型,

let f = undefined :: (CompNode Int)

然后,我尝试pshow :: PrettyShow a => a -> String在它上面调用我的函数。我收到此错误消息。

> pshow f

<interactive>:1:1:
    Overlapping instances for PrettyShow (CompNode Int)
      arising from a use of `pshow'
    Matching instances:
      instance (G.Graph g, PrettyShow (G.Vertex g)) => PrettyShow g
        -- Defined at Graph.hs:61:10-57
      instance (PrettyShow a, Show a) => PrettyShow (CompNode a)
        -- Defined at Interpreter.hs:61:10-58

问题是那CompNode Int不是图表,所以我认为第一个匹配实例不应该触发。(第二个是我想要执行的。)事实上,如果我写一个函数,它的参数是一个图,

> :{
| let g :: G.Graph a => a -> a
|     g = id
| :}

然后在f上调用它,我得到预期的 no instance 错误消息,

> g f

<interactive>:1:1:
    No instance for (G.Graph (CompNode Int))

在此先感谢,抱歉众包。我正在使用 GHC 7.0.4。

4

1 回答 1

4

问题是 CompNode Int 不是图,所以我认为第一个匹配实例不应该触发。

你会这么想,但不幸的是它不是那样工作的。

GHC 在选择实例时,只看头部,即类名后面的部分。只有在实例选择完成后,它才会检查上下文,即 . 之前的部分=>。上下文中的不匹配可能会导致实例被拒绝并导致类型检查错误,但它们不会导致 GHC 回溯并寻找另一个实例。

因此,鉴于这些情况:

instance (G.Graph g, PrettyShow (G.Vertex g)) => PrettyShow g

instance (PrettyShow a, Show a) => PrettyShow (CompNode a)

...如果我们忽略上下文,它们看起来像这样:

instance PrettyShow g

instance PrettyShow (CompNode a)

这应该清楚地表明,第一个实例是完全通用的,并且绝对重叠了所有内容。

在某些情况下,您可以使用OverlappingInstances扩展,但这不会改变上述行为;相反,它允许 GHC 通过选择唯一最具体的实例来解决模棱两可的实例(如果存在)。但是使用重叠实例可能会很棘手并且会导致隐秘的错误,所以我鼓励你首先重新考虑设计,看看你是否可以完全避免这个问题。

也就是说,在这里的特定示例中,CompNode a确实是一个明确更具体的匹配CompNode Int,因此 GHC 会选择它而不是一般实例。

于 2011-07-25T02:00:22.590 回答