IncoherentInstances如何工作?
玩一些代码 :
{-# LANGUAGE FlexibleInstances, OverlappingInstances #-} class Arity f where arity :: f -> Int instance Arity x where arity _ = 0 instance Arity f => Arity ((->) af) where arity f = 1 + arity (f undefined)
没有IncoherentInstances
:
ghci> arity foldr blah blah ambiguous blah blah possible fix blah ghci> arity (foldr :: (a -> Int -> Int) -> Int -> [a] -> Int) 3 ghci> let fxy = 3 in arity f 2 ghci> arity $ \xy -> 3 2
如果我们将IncoherentInstances
添加到编译指示列表中,那么它可以处理foldr
而不需要单形签名,但是在lambdas上得到了错误的答案:
ghci> arity foldr 3 ghci> let fxy = 3 in arity f 2 ghci> arity $ \xy -> 3 -- should be 2 0
非相干实例背后的黑魔法是什么? 为什么它在这里做什么?
那么这是相当复杂的。 让我们从模棱两可的错误开始:
<interactive>:1:1: Ambiguous type variable `b0' in the constraint: (Arity b0) arising from a use of `arity' Probable fix: add a type signature that fixes these type variable(s) In the expression: arity foldr In an equation for `it': it = arity foldr
通常情况下,如果没有重叠的实例,当试图将一个types与一个类进行匹配时,它会将该types与该类的所有实例进行比较。 如果只有一个匹配,它将使用该实例。 过分的,你会得到一个没有实例的错误(例如show (*)
),或重叠的实例错误。 例如,如果您从上述程序中删除了OverlappingInstances
语言function,则会出现arity (&&)
这个错误:
<interactive>:1:1: Overlapping instances for Arity (Bool -> Bool -> Bool) arising from a use of `arity' Matching instances: instance Arity f => Arity (a -> f) -- Defined at tmp/test.hs:9:10-36 instance Arity x -- Defined at tmp/test.hs:12:10-16 In the expression: arity (&&) In an equation for `it': it = arity (&&)
它与Arity (a -> f)
匹配,可以是Bool
, f
可以是Bool -> Bool
。 它也匹配Arity x
,因为x
可以是Bool -> Bool -> Bool
。
有了OverlappingInstances
,当遇到两个或多个实例可以匹配的情况时,如果有一个最具体的实例,它将被选中。 如果X
可以匹配Y
,则实例X
比实例Y
更具体,但反之亦然。
在这种情况下, (a -> f)
匹配x
,但是x
不匹配(a -> f)
(例如考虑x
是Int
)。 所以Arity (a -> f)
比Arity x
更具体,所以如果两者匹配,前者将被选中。
使用这些规则, arity (&&)
将首先匹配Arity ((->) af)
, a
是Bool
,而f
是Bool -> Bool
。 下一场比赛将有a
Bool
和f
是布尔。 最后它会结束与Arity x
匹配, x
是Bool。
注意上面的函数, (&&)
result是一个具体的typesBool
。 当这个types不具体时,会发生什么? 例如,让我们看看arity undefined
的arity undefined
的结果。 undefined
具有typesa
,所以它不是一个具体的types:
<interactive>:1:1: Ambiguous type variable `f0' in the constraint: (Arity f0) arising from a use of `arity' Probable fix: add a type signature that fixes these type variable(s) In the expression: arity undefined In an equation for `it': it = arity undefined
你会得到一个不明确的typesvariables错误,就像foldr那样。 为什么会这样呢? 这是因为取决于什么是一个不同的实例。 如果a
是Int
,那么应该匹配Arity x
实例。 如果a
是Int -> Int
,则应该匹配Arity ((->) af)
实例。 由于这个,ghc拒绝编译程序。
如果你注意到foldr的types: foldr :: forall a b. (a -> b -> b) -> b -> [a] -> b
foldr :: forall a b. (a -> b -> b) -> b -> [a] -> b
,您会注意到同样的问题:结果不是一个具体的variables。
这里是IncoherentInstances
进来的地方:在启用该语言function的情况下,它将忽略上述问题,并select一个总是与variables匹配的实例。 例如,如果arity undefined
, Arity x
将始终与a
匹配,所以结果将为0.类似的事情在foldr
处完成。
现在对于第二个问题,为什么当IncoherentInstaces
被启用时, arity $ \xy -> 3
返回0?
这是非常奇怪的行为。 这下面的ghci会话将显示它是多么奇怪:
*Main> let fab = 3 *Main> arity f 2 *Main> arity (\ab -> 3) 0
这导致我认为在ghc中存在一个错误,其中\ab -> 3
被IncoherentInstances
看作具有typesx
而不是a -> b -> Int
。 我想不出为什么这两个expression式不应该完全一样。