使用some code:
{-# LANGUAGE FlexibleInstances, OverlappingInstances #-}
class Arity f where
arity :: f -> Int
instance Arity x where
arity _ = 0
instance Arity f => Arity ((->) a f) 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 f x y = 3 in arity f
2
ghci> arity $ \x y -> 3
2
如果我们将IncoherentInstances
添加到编译指示列表中,那么它可以处理foldr
而不需要单态类型签名,但它在lambdas上得到了错误的答案:
ghci> arity foldr
3
ghci> let f x y = 3 in arity f
2
ghci> arity $ \x y -> 3 -- should be 2
0
Incoherent Instances背后的黑魔法是什么?为什么它会做它在这里做的事情?
答案 0 :(得分:30)
这很复杂。让我们从含糊不清的错误开始:
<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
通常,在没有重叠实例的情况下,当尝试将类型与类匹配时,它会将类型与该类的所有实例进行比较。如果只有一个匹配,它将使用该实例。过度使用您将获得无实例错误(例如,使用show (*)
)或重叠实例错误。例如,如果您从上述程序中删除OverlappingInstances
语言功能,则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)
,a
可以是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 ((->) a f)
,a
为Bool
,f
为Bool -> Bool
。下一场比赛a
为Bool
,f
为bool。最后,它将结束匹配Arity x
,x
为Bool。
注意上面的函数,(&&)
结果是具体类型Bool
。但是当类型不具体时会发生什么?例如,让我们看一下arity undefined
的结果。 undefined
的类型为a
,因此它不是具体类型:
<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
你得到一个不明确的类型变量错误,就像foldr那样。为什么会这样?这是因为取决于a
是什么,将需要不同的实例。如果a
为Int
,则Arity x
实例应匹配。如果a
为Int -> Int
,则Arity ((->) a f)
实例应匹配。因此,ghc拒绝编译程序。
如果你注意到foldr的类型:foldr :: forall a b. (a -> b -> b) -> b -> [a] -> b
,你会注意到同样的问题:结果不是具体的变量。
以下是IncoherentInstances
的用法:启用该语言功能后,它将忽略上述问题,只选择一个始终与变量匹配的实例。例如arity undefined
,Arity x
将始终与a
匹配,因此结果将为0.类似的事情在foldr
处完成。
现在针对第二个问题,为什么arity $ \x y -> 3
在启用IncoherentInstaces
时返回0?
这是非常奇怪的行为。以下ghci会话将显示它有多奇怪:
*Main> let f a b = 3
*Main> arity f
2
*Main> arity (\a b -> 3)
0
这让我认为ghc中存在一个错误,\a b -> 3
看到IncoherentInstances
的类型为x
而不是a -> b -> Int
。我想不出为什么这两个表达式不应该完全相同的任何原因。