Haskell 类型族和非相干实例之间的奇怪交互

Haskell 类型族和非相干实例之间的奇怪交互,haskell,ghc,typeclass,type-families,Haskell,Ghc,Typeclass,Type Families,此代码示例未编译 {-# LANGUAGE TypeFamilies, FlexibleInstances, UndecidableInstances, ScopedTypeVariables #-} module IncoherentBug where type family F a where F () = Int F a = a class C a where c :: a -> Int instance C Int where c y = y

此代码示例未编译

{-# LANGUAGE TypeFamilies, FlexibleInstances, UndecidableInstances, ScopedTypeVariables #-}
module IncoherentBug where

type family F a where
    F () = Int
    F a = a

class C a where
    c :: a -> Int

instance C Int where
    c y = y

instance {-# INCOHERENT #-} Monoid a => C a where
    c _ = 0

class TwoPossible a where
    x :: a

instance a ~ () => TwoPossible [a] where
    x = []

instance TwoPossible Bool where
    x = False

f :: (F a -> Int) -> [a] -> ()
f _ _ = ()


test = f (\v -> c v) x
基本上这里发生的是
f
的签名请求将
x
的类型解析为
[()]
,然后
v
的类型是
f()
,即
Int
,最后应该选择
C
的第一个实例。取而代之的是,我得到了一个缺少的
monoidint
实例错误

当我将
不连贯
实例更改为
可重叠
实例时,代码编译良好。如果我用
Int
F()
注释
v
,它也会起作用。如果我用
[()]
注释
x
(作为f的参数),它也可以工作

这是个错误还是我误解了什么?ghc mod报告
v
的类型
F()
,即使我没有这样注释它。此外,错误消息提到了一个
Int
,这意味着类型检查器为
v
找到了正确的类型,但出于某种原因,没有选择更具体的实例


我还应该注意,我使用的是GHC8。我不知道此问题是否出现在早期版本中。

GHC完全正确地拒绝此代码。您有一个
C(fa)
约束,它是由

f c :: C (F a) => [a] -> ()
当您启用“非相干”时,GHC将立即将其降低到

f c :: Monoid (F a) => [a] -> ()
甚至不考虑争论的类型。这就是非一致性的含义——实例化可以提供更具体的实例,但非一致性实例无论如何都是匹配的。当然还有实例
…=>cA
匹配每种类型,因此如果您的
C
约束出现在任何地方,该实例将立即匹配

对于可重叠的
等,不能通过选择
幺半群a=>ca
实例来减少
C(fa)
约束,因为
C Int
实例也可以匹配(这是一致性,与非一致性相反)


如果您想了解自己,请向GHC询问
fc
的推断类型,其中
不连贯
可重叠

想必GHC很早就为
C
选择了一个实例,这是它有权做的,因为它选择的实例被标记为
不连贯
。@ReidBarton不会让不连贯的实例变得毫无意义,因为它基本上意味着“总是选择这个实例”?当您想要
实例cxy
实例cxy
时,非相干实例非常有用,并且您不关心为
实例cxy
选择哪个实例。重叠的实例有助于让我像看鞋上的狗屎一样看你的代码。@d显然,你也不必关心为
cz W
选择哪一个。不,这是完全不相关的。你的
不连贯的
实例是完全通用的;你没有类型构造函数,你完全正确。似乎当
x
尚未应用时,GHC在达到
fc
时会立即解决尽可能多的问题。事实上,如果我切换
f
的参数顺序,那么我的代码可以工作,第二个实例被选中。