考虑以下代码:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
class X a
class Y a
instance Y Bool
instance (Y a) => X a
instance {-# OVERLAPPING #-} X Int
f :: (X a) => a -> a
f x = x
编写上述实例需要这些LANGUAGE杂注。
现在,假设我们想写一个函数g:
g :: (Y a) => a -> a
g = f
如果没有非相干实例或向其中一个实例添加{-#INC相干#-},则不会进行类型检查。但当我们添加这个,并询问ghci
ghci> :t f
f :: Y a => a -> a
突然"f"的类型变了?
在这个小例子中,当我给f一个Int时,程序仍然会进行类型检查(这表明上面的内容只是一个"视觉错误",但在一个更大的例子中,程序不会进行类型检查,给我一个错误,比如:
Could not deduce (Y a) arising from a use of 'f
(...)
from the context: (..., X a, ...)
当我们说时也会发生这种情况
h = f
并尝试用Int 调用h
:type f
不报告已定义实体f
的类型。它报告表达式f
的类型。GHC非常努力地在表达式中消除多态性。特别地,在表达式中使用f
会触发X a
约束的简化(就像使用具有约束的任何定义一样(。如果没有IncoherentInstances
,GHC就不会使用instance Y a => X a
,因为还有另一个实例与它重叠,所以GHC需要等待,看看应该使用哪个实例。这确保了连贯性;曾经使用过的仅X Int
实例是显式"专用"实例。对于IncoherentInstances
,你说你不关心一致性,所以GHC继续使用多态实例将X a
简化为Y a
,只要f
出现在表达式中。你看到的奇怪行为——有时GHC可以使用X Int
,有时抱怨没有Y Int
——是GHC在何时简化约束方面做出不同的内部决定的结果(你确实要求不一致!(。查看定义的类型的命令是:type +v
。:type +v f
应显示"已声明"的f
的类型。希望您也能看到IncoherentInstances
是个坏主意。不要用它。