使用 PolyKind 和类型族时的种类歧义

Kind ambiguity when using PolyKinds and type families

我有两个类型家族,其中一个将一种类型映射到另一种不同类型和多态函数的类型:

{-# LANGUAGE PolyKinds, TypeFamilies, FlexibleContexts, ScopedTypeVariables #-} 

type family F (a :: k1) :: k2
type family G (a :: k2) :: *

f :: forall k1 k2 (a :: k1) (p :: k1 -> *) . p (a :: k1) -> G (F (a :: k1) :: k2)
f = undefined

此代码未进行类型检查并出现以下错误消息:

• Couldn't match type ‘G k20 (F k20 k1 a)’ with ‘G k2 (F k2 k1 a)’
  Expected type: p a -> G k2 (F k2 k1 a)
    Actual type: p a -> G k20 (F k20 k1 a)
  NB: ‘G’ is a non-injective type family

但我不明白歧义从何而来,我该如何指定缺失的种类?

当我只使用一种类型时,它起作用了:

g :: forall k1 k2 (a :: k1) (p :: k1 -> *) (q :: k2 -> *). p (a :: k1) -> q (F (a :: k1) :: k2)
g = undefined
f :: forall k1 k2 (a :: k1) (p :: k1 -> *). p a -> G (F a :: k2)

让我试着说:

x :: [String]
x = f (Just 'a')

这将使用 k1 ~ Typea ~ Charp ~ Maybe

实例化 f
f :: forall k2. Maybe Char -> G (F Char :: k2)

现在呢?好吧,我还需要 G (F Char :: k2) ~ [String],但是 G 是一个非单射类型族,所以不知道它的参数——k2F Char :: k2——应该是什么。所以,x的定义是错误的; k2 是不明确的,不可能为它推断实例化。

但是,您可以非常清楚地看到没有 f 的用法 ever 能够推断出 k2.原因是 k2 只出现在非单射类型家族应用程序下面的 f 类型中(另一个 "bad position" 是 => 的 LHS)。它从不出现在可以推断的位置。因此,如果没有像 TypeApplications 这样的扩展名,f 无用的 ,并且永远不会在不引发此错误的情况下提及。 GHC 检测到这一点并在定义而不是用法上引发错误。您看到的错误消息与您尝试时遇到的错误大致相同:

f0 :: forall k10 k20 (a :: k10) (p0 :: k10 -> *). p0 a0 -> G (F a0 :: k20)
f0 = f

这会产生相同类型的不匹配,因为没有理由 f0k20 必须匹配 f1k2

您可以通过启用 AllowAmbiguousTypes 来消除 f 定义中的错误,这会禁用对所有定义的无用性检查。但是,仅此一项,这只会将错误推向 f 的每次使用。为了实际调用 f,您应该启用 TypeApplications:

f0 :: forall k10 k20 (a :: k10) (p0 :: k10 -> *). p0 a0 -> G (F a0 :: k20)
f0 = f @k10 @k20 @a0 @p0

TypeApplications 的替代方法类似于 Data.Proxy.Proxy,但它几乎已经过时了,除了在更高级别的上下文中。 (即便如此,一旦我们有了 type-lambdas 之类的东西,它就真的无法工作了。)

歧义检查最初是为了拒绝无法调用的函数,因为类型参数和约束无法从显式函数参数中推断出来。

然而,从 GHC 8.6.x 开始,就没有这样的函数了,因为一切都可以通过 TypeApplications 明确化。我建议只启用 AllowAmbiguousTypesTypeApplications。 GHC 关于模棱两可类型的警告本身并不是很有用,因为它拒绝了类型应用程序的许多有效用例。