IncoherentInstances如何运作?

时间:2011-12-03 22:20:46

标签: haskell ghc typeclass

使用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背后的黑魔法是什么?为什么它会做它在这里做的事情?

1 个答案:

答案 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可以是Boolf可以是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)aBoolfBool -> Bool。下一场比赛aBoolf为bool。最后,它将结束匹配Arity xx为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是什么,将需要不同的实例。如果aInt,则Arity x实例应匹配。如果aInt -> Int,则Arity ((->) a f)实例应匹配。因此,ghc拒绝编译程序。

如果你注意到foldr的类型:foldr :: forall a b. (a -> b -> b) -> b -> [a] -> b,你会注意到同样的问题:结果不是具体的变量。


以下是IncoherentInstances的用法:启用该语言功能后,它将忽略上述问题,只选择一个始终与变量匹配的实例。例如arity undefinedArity 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。我想不出为什么这两个表达式不应该完全相同的任何原因。