此代码示例无法编译。
{-# 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
的类型为{ {1}} F ()
,最后应该选择Int
的第一个实例。相反,我得到的是C
实例错误。
当我将Monoid Int
实例更改为INCOHERENT
实例时,代码编译正常。如果我使用OVERLAPPABLE
或v
注释Int
,它也会有效。如果我使用F ()
注释x
(作为f的参数),它也可以。
这是一个错误还是我在这里误解了什么?即使我没有对[()]
进行注释,ghc-mod也会报告F ()
的{{1}}类型。除了错误消息提到v
这意味着类型检查器找出Int
的正确类型,但由于某种原因没有选择更具体的实例。
我也应该注意到我使用的是GHC 8.我不知道这个问题是否出现在早期版本中。
答案 0 :(得分:1)
GHC完全正确拒绝此代码。您有一个C (F a)
约束,它来自
f c :: C (F a) => [a] -> ()
当您启用INCOHERENT
时,GHC会立即将其减少为
f c :: Monoid (F a) => [a] -> ()
甚至没有考虑参数的类型。这就是不连贯意味着什么 - 实例化可以提供更具体的实例,但是不连贯的实例无论如何都会匹配。当然,实例... => C a
匹配每个类型,因此如果您的C
约束出现在任何地方,那么该实例将立即匹配。
使用OVERLAPPABLE
之类的,C (F a)
约束不能通过选择Monoid a => C a
实例来减少,因为C Int
实例可以匹配同样(这是连贯性,与不连贯性相反)。
如果您想看到自己,请向GHC询问f c
与INCOHERENT
和OVERLAPPABLE
的推断类型。