类型族、GADT 和命名记录的编译错误

Compile error with type families, GADTs and named records

在下面的代码中,T1T2 编译正常,但 T3 失败:

{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeFamilies #-}

type family F a

data T1 b where
  T1 :: a -> T1 (F a)

data T2 b where
  T2 :: { x2 :: a } -> T2 a

data T3 b where
  T3 :: { x3 :: a } -> T3 (F a)

我正在尝试了解原因。 T3 只是 T1 但有一个命名记录。这似乎并没有那么特别,因为无论如何都可以使用构造函数语法来提取它。

这些例子可能看起来很傻,但在我的代码中有对 a 的约束,例如(Show a),因此这些值可以在提取时使用。

让我们忘记 T2T3,并尝试为 T1 定义一个提取函数。类型应该是什么?

x1 :: ???
x1 (T1 a) = a

嗯,你可能会猜到 x1 :: T1 (F a) -> a。但这是不对的,如果您尝试这样做,您将得到与定义 T3.

相同的错误

问题是,如果有人给你一个 T1 T,而你碰巧知道一个类型 A,使得 F AT,你不能得出结论T1 T 包含类型 A 的值,因为它可以包含另一个类型 B,其中 F B 等于 T。作为极端情况,假设我们有

type instance F _ = ()

然后如果我们按照我们的猜测 x1 :: T1 (F a) -> a,我们将有

T1 :: a -> T1 ()
x1 :: T1 () -> b

并且组合这些会让我们写出 a -> b,这很糟糕。

x1 的真实类型类似于 existential-providing-constraint

T1 t -> (exists a. (F a ~ t) *> a)

哪个 GHC 不支持。

T3 的问题实际上与

的问题相同
data T3' where T3' :: { x3' :: a } -> T3'

您可以使用模式匹配提取字段(如果有更多字段或约束,这可能会有用),但不能使用记录选择器或函数。