类型族与不连贯实例之间的奇怪交互

时间:2016-09-23 17:54:37

标签: 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

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实例时,代码编译正常。如果我使用OVERLAPPABLEv注释Int,它也会有效。如果我使用F ()注释x(作为f的参数),它也可以。

这是一个错误还是我在这里误解了什么?即使我没有对[()]进行注释,ghc-mod也会报告F ()的{​​{1}}类型。除了错误消息提到v这意味着类型检查器找出Int的正确类型,但由于某种原因没有选择更具体的实例。

我也应该注意到我使用的是GHC 8.我不知道这个问题是否出现在早期版本中。

1 个答案:

答案 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 cINCOHERENTOVERLAPPABLE的推断类型。