在未扩展的 Haskell 中,声明
data A = B
定义了两个新实体,一个在计算级别,一个在类型级别:
- 在类型级别上,定义了一个名为
A
的新基础类型(kind为*
),
- 在计算级别上,定义了一个名为
B
的新基础计算(类型为A
)。
当您开启 DataKinds
时,该声明生效。
data A = B
现在定义了四个新实体,一个在计算级别,两个在类型级别和一个在类别级别:
- 在类别级别,有一个新的基本类别
A
,
- 在类型级别,有一个新的基本类型
'B
(属于类别 A
),
- 在类型级别,有一个新的基本类型
A
(属于类别 *
),以及
- 在计算级别,有一个新的基本计算
B
(类型为 A
)。
这是我们之前拥有的严格超集:旧的 (1) 现在是 (3),旧的 (2) 现在是 (4)。
这些新实体解释了您所描述的以下交互作用:
:type Zero
Zero :: Nat
:kind 'Zero
'Zero :: Nat
:type 'Zero
Syntax error on 'Zero
我认为它很清楚地解释了前两个。它解释了最后一个的原因是因为'Zero
是一种类型级别的东西——你不能询问一个类型的类型,只能询问一个计算的类型!
现在,在Haskell中,每当一个名称出现时,从周围的语法中就可以清楚地知道该名称是计算级别的名称、类型级别的名称还是种级别的名称。因此,必须在类型级别上包含'B
中的撇号,这有点麻烦——毕竟,编译器知道我们处于类型级别,因此不能引用未提升的计算级别的B
。因此,为了方便起见,当这是明确的时候,可以省略撇号。
从现在开始,我将区分“后端”——那里只有上述四个实体,而且始终不含歧义——和“表面语法”,即您键入到文件中并传递给GHC的东西,包括歧义,但更方便。使用这个术语,可以写出以下内容,具有以下含义:
Surface syntax Level Back end
Name computation Name
Name type Name if that exists; 'Name otherwise
'Name type 'Name
Name kind Name
---- all other combinations ---- error
这解释了你所经历的第一个互动(也是上面未解释的唯一一个):
:kind Zero
Zero :: Nat
由于:kind
必须应用于类型级别的事物,编译器知道表面语法名称Zero
必须是类型级别的事物。由于没有类型级别的后端名称Zero
,它尝试使用'Zero
,并成功匹配。
那么它为什么会出现歧义呢?好的,注意上面我们通过一次声明在类型级别定义了两个新实体。为了方便介绍,我给等式左右的新实体命名不同的内容。但如果我们稍微调整一下声明,会发生什么呢:
data A = A
我们仍然引入了四个新的后端实体:
- 类型为
A
的种类 A
,
- 类型为
A
(属于种类为 A
)的类型 'A
,
- 类型为
*
的种类 A
的类型 A
,以及
- 类型为
A
的计算 A
。
糟糕!在类型层面上现在有一个 'A
和一个 A
。如果在表面语法中省略了撇号,它将使用(3),而不是(2) -- 您可以通过表面语法 'A
明确选择 (2)。
更重要的是,这不必全部发生在单个声明中。一条声明可以生成带撇号的版本,另一条声明可以生成不带撇号的版本;例如:
data A = B
data C = A
本文将介绍第一个声明中的类型级后端名称A
和第二个声明中的类型级后端名称'A
。
:kind 'Zero
而不是:kind Zero
。每当名称Zero
出现在类型级别中时,编译器都会将其替换为'Zero
? - JoeChoiZero
是否存在,只有当它不存在时才会检查'Zero
是否存在。 - Daniel WagnerZero
可能对应于后端的Zero
或'Zero
,具体取决于何种声明在范围内。 - Daniel Wagnerdata Zero
声明会导致名称冲突,如果不使用'Zeor
而是使用Zero
。 - JoeChoi