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
的参数顺序,那么我的代码可以工作,第二个实例被选中。