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)匹配,可以是Boolf可以是Bool -> Bool 。 它也匹配Arity x ,因为x可以是Bool -> Bool -> Bool

有了OverlappingInstances ,当遇到两个或多个实例可以匹配的情况时,如果有一个最具体的实例,它将被选中。 如果X可以匹配Y ,则实例X比实例Y更具体,但反之亦然。

在这种情况下, (a -> f)匹配x ,但是x不匹配(a -> f) (例如考虑xInt )。 所以Arity (a -> f)Arity x更具体,所以如果两者匹配,前者将被选中。

使用这些规则, arity (&&)将首先匹配Arity ((->) af)aBool ,而fBool -> Bool 。 下一场比赛将有a Boolf是布尔。 最后它会结束与Arity x匹配, x是Bool。


注意上面的函数, (&&) result是一个具体的typesBool 。 当这个types不具体时,会发生什么? 例如,让我们看看arity undefinedarity 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那样。 为什么会这样呢? 这是因为取决于什么是一个不同的实例。 如果aInt ,那么应该匹配Arity x实例。 如果aInt -> 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 undefinedArity 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 -> 3IncoherentInstances看作具有typesx而不是a -> b -> Int 。 我想不出为什么这两个expression式不应该完全一样。