考虑以下程序,它仅在启用不连贯实例的情况下编译:
{-# LANGUAGE TypeFamilies, MultiParamTypeClasses, FlexibleInstances #-}
{-# LANGUAGE IncoherentInstances #-}
main = do
print (g (undefined :: Int))
print (g (undefined :: Bool))
print (g (undefined :: Char))
data True
class CA t where
type A t; type A t = True
fa :: t -> String
instance CA Int where
fa _ = "Int"
class CB t where
type B t; type B t = True
fb :: t -> String
instance CB Bool where
fb _ = "Bool"
class CC t where
type C t; type C t = True
fc :: t -> String
instance CC Char where
fc _ = "Char"
class CAll t t1 t2 t3 where
g :: (t1 ~ A t, t2 ~ B t, t3 ~ C t) => t -> String
instance (CA t) => CAll t True t2 t3 where g = fa
instance (CB t) => CAll t t1 True t3 where g = fb
instance (CC t) => CAll t t1 t2 True where g = fc
当编译没有不连贯的实例时,它声称多个实例匹配。这似乎意味着当允许不连贯的实例时,将任意选择实例。
除非我非常幸运,否则这将导致编译错误,因为在大多数情况下不会满足实例约束。
但是对于不连贯的实例,我没有得到任何编译错误,并且确实得到了选择“正确”实例的以下输出:
"Int"
"Bool"
"Char"
所以我只能在这里总结几件事之一:
- GHC 正在回溯实例上下文故障(它说它在自己的文档中没有这样做)
- GHC 实际上知道只有一个匹配的实例,但没有足够的勇气使用它,除非打开不连贯的实例
- 我刚刚非常幸运(三分之一3 = 27 机会)
- 其他事情正在发生。
我怀疑答案是 4(可能与 2 结合)。我想要任何答案来解释这里发生了什么,以及在不连贯的实例中我可以在多大程度上依赖这种行为?如果它是可靠的,那么我似乎可以使用这种行为来制作相当复杂的类层次结构,实际上它的行为有点像子类型化,例如我可以说所有类型的 A 类和 B 类都在 C 类中,并且实例编写者可以为A 无需显式地为 C 创建一个实例。
编辑:
我怀疑答案与GHC 文档中的这个有关:
- 找到与目标约束匹配的所有实例 I;也就是说,目标约束是 I 的替换实例。这些实例声明是候选。
- 消除以下两项都成立的任何候选人 IX:
- 还有另一个更具体的候选 IY;也就是说,IY 是 IX 的替代实例,反之则不然。
- IX 是可重叠的,或者 IY 是重叠的。(这种“要么/或”设计,而不是“两者/和”设计,允许客户端故意覆盖库中的实例,而无需更改库。)
如果只剩下一个不连贯的候选者,则选择它。如果所有剩余的候选人都语无伦次,请选择一个任意的候选人。否则搜索失败(即当不止一个幸存的候选者不是不连贯的时候)。
如果所选候选者(来自上一步)不连贯,则搜索成功,并返回该候选者。
如果不是,则查找与目标约束一致但不匹配的所有实例。当目标约束被进一步实例化时,此类非候选实例可能匹配。如果全部不连贯,则搜索成功,返回选中的候选;如果不是,则搜索失败。
如果我错了,请纠正我,但无论是否选择了不连贯的实例,在第一步中,只有一个实例“匹配”。在不连贯的情况下,我们在第 4 步与那个“匹配”的情况发生冲突。但在不连贯的情况下,我们然后转到第 4 步,即使只有一个实例“匹配”,我们发现其他实例“统一” . 所以我们必须拒绝。
这种理解正确吗?
如果是这样,有人可以准确解释“匹配”和“统一”的含义,以及它们之间的区别吗?