原书作者:Luca Cardelli,Microsoft Research
本文整理:Koshiba
译者注释使用下划线标注,方便识别。
接续上一篇于国庆期间发布的类型系统综述,本文将简要介绍有类型 λ \lambda λ 演算 的类型系统,以及如何在此基础上扩充 U n i t Unit Unit 类型(单一, 并不知道如何翻译,感觉单一类型比单位类型好听;且毕竟不可能是单子类型——单子这个词一般留给了 monad。), B o o l Bool Bool 类型(布尔), N a t Nat Nat 类型(自然数), P r o d u c t Product Product 类型(积), U n i o n Union Union 类型(联合), R e c o r d Record Record 类型(记录), V a r i a n t Variant Variant 类型(变体)。
本文涵盖了 Type Systems by Luca Cardelli 1 的第三章(一阶类型系统)的大部分内容。因第三章的内容,特别是 公式 较多,我们将余下的 Reference 类型(引用),Recursive 类型(递归)以及数组和列表留给下次。全文没有使用 \mathit{}
,留给渲染器,看造化了。
一阶类型系统和有类型 λ \lambda λ- 演算
在大多数常见的过程式语言中发现的类型系统都是一阶的。从类型理论特有的术语来看,它囊括了(令人困惑的)高阶函数,但缺乏类型参数化和类型抽象——而这是二阶(类型系统)的特征。历史上的 Pascal 和 Algol 68 有丰富的一阶类型系统,而 Fortran 和 Algol 60 的类型系统就比较差了。
我们可以给无类型的 λ λ λ- 演算一个最小的一阶类型系统。其中无类型的 λ λ λ 抽象 λ x . M λx.M λx.M 表示参数为 x x x 、返回 M M M 的函数。类型化该演算系统 只需要函数类型和一些基本类型;我们将在后面看到如何增加其他常见的类型结构。
一阶类型化的 λ λ λ- 演算被称为系统 F1。 与无类型的 λ λ λ- 演算相比,主要的变化是为 λ λ λ- 抽象(即函数定义)增加了类型标注,它的语法是 λ x : A . M λx:A. M λx:A.M,其中 x x x 是函数参数, A A A 是参数的类型,而 M M M 是函数体。(在一个类型化的编程语言中,我们可能会标注函数体的类型,但在这里没有必要。)从 λ x . M λx. M λx.M 到 λ x : A . M λx:A.M λx:A.M 是任何从无类型语言到类型化语言的典型 < u>(但不是必须):约束变量具有类型标注。
由于 F1 主要基于函数,其中最有趣的类型也是函数类型。 A → B A \to B A→B 是具有 A A A 型参数和 B B B 型结果的函数类型。我们还需要一些基本类型来建立函数类型,我们用 B a s i c Basic Basic 表示这种基本类型的集合,用 K ∈ B a s i c K \in Basic K∈Basic 表示任意一种基本类型。(就目前来说,设立基本类型纯粹是技术上的需要,但不久我们将考虑有趣的基本类型,如 B o o l Bool Bool 和 N a t Nat Nat。)
下面给出了 F1 的语法。对类型化语言中的语法进行简单的描述是很重要的。在无类型的 λ λ λ- 演算的情况下,上下文无关文法(context-free grammars)描述了所有合法的程序。但在类型化的 λ λ λ- 演算中却不是这样,因为良好的行为(通常)不具备 “上下文无关” 这一美好的性质——描述合法程序的任务由类型系统来承担。例如, λ x : K . x ( y ) λx:K.x(y) λx:K.x(y) 尊重下面给出的语法,却也不是 F1 的程序,因为它不是类型良好的——这里 K K K 不是一个函数类型 < u>(但具有 K K K 类型的 x x x 却被当做函数使用)。为了定义自由变量和约束变量的概念以及语言的作用域规则,上下文无关文法依然是必要的。基于作用域规则,那些只在约束变量上有差异的项,如 λ x : K . x λx:K.x λx:K.x 和 λ y : K . y λy:K.y λy:K.y,在语法上是相同的。这种方便的识别在类型规则中是隐含的 < u>(且为了应用某些类型规则,我们可能必须重新命名约束变量)。
A , B : : = 类型 K K ∈ B a s i c 基本类型 ∣ A → B 函数类型 \begin{aligned} A, B ::=& \qquad &\text{类型} \\ &\ K \quad K \in Basic \qquad &\text{基本类型} \\ \mid &\ A \to B \qquad &\text{函数类型} \end{aligned} A,B::=∣ KK∈Basic A→B 类型基本类型函数类型
M , N : : = 项 x 变量 ∣ λ x : A . M 函数, λ 抽象 ∣ M N 函数应用,函数调用 \begin{aligned} M, N ::=&\ \qquad &\text{项} \\ & \ x \qquad &\text{变量}\\ \mid &\ \lambda x:A.\, M \qquad &\text{函数,$\lambda$ 抽象}\\ \mid &\ M\ N \qquad &\text{函数应用,函数调用} \end{aligned} M,N::=∣∣ x λx:A.M M N项变量函数,λ 抽象函数应用,函数调用
(F1 的自由变量的定义与无类型的 λ λ λ- 演算基本相同,只需额外地将类型标注忽略即可。)
F1 只需要三个简单的断言,见下表。断言 Γ ⊢ A \Gamma \vdash A Γ⊢A 在某种意义上是多余的,因为所有语法正确的类型 A A A 在任何环境 Γ \Gamma Γ 中都是良好的。(然而,在二阶系统中,类型是否良好并不能仅仅通过语法来体现,因此判断 Γ ⊢ A \Gamma \vdash A Γ⊢A 变得至关重要。)但我们现在还是包括了这个断言,这样以后的扩展会比较容易。
Γ ⊢ ⋄ 环境 Γ 是定义良好的 Γ ⊢ A 类型 A 在环境 Γ 下是定义良好的 Γ ⊢ M : A M 是在环境 Γ 下具有类型 A 的定义良好的项 \begin{aligned} & \Gamma \vdash \diamond \qquad & \text{环境 $\Gamma$ 是定义良好的} \\ & \Gamma \vdash A \qquad & \text{类型 $A$ 在环境 $\Gamma$ 下是定义良好的} \\ & \Gamma \vdash M : A \qquad & \text{$M$ 是在环境 $\Gamma$ 下具有类型 $A$ 的定义良好的项} \end{aligned} Γ⊢⋄Γ⊢AΓ⊢M:A环境 Γ 是定义良好的类型 A 在环境 Γ 下是定义良好的M 是在环境 Γ 下具有类型 A 的定义良好的项
这些断言的有效性由下表中的规则给出。
-
规则(Env)是唯一一个不需要假设的规则(即,它是 唯一的公理)。它指出,空环境是一个有效的环境。
-
规则(Env x)用于将环境 Γ \Gamma Γ 扩展到一个更大的环境 Γ , x : A \Gamma, x:A Γ,x:A,条件是 A A A 是 Γ Γ Γ 中的一个有效类型。请注意,当我们假设 Γ ⊢ A \Gamma \vdash A Γ⊢A 成立时也意味着 Γ Γ Γ 必须是定义良好的(根据归纳法)。也就是说,在推导 Γ ⊢ A Γ \vdash A Γ⊢A 的过程中,我们必须先推导出 Γ \Gamma Γ 。这个规则(即 Env x)的另一个要求是,变量 x x x 不得在 Γ \Gamma Γ 中定义。
-
我们要注意保持变量在环境中的独立性,所以当 Γ , x : A ⊢ M : B \Gamma, x:A \vdash M : B Γ,x:A⊢M:B 被推导出时,如(Val Fun)的假设,我们知道 x x x 不能出现在 d o m ( Γ ) dom(\Gamma) dom(Γ) 中。(如果 x x x 已经在 d o m ( Γ ) dom(\Gamma) dom(Γ) 中出现,则我们可以通过对 λ λ λ- 抽象(即函数)的形参 x x x 重命名来避免冲突并使得规则可以应用,如上文所述。)
-
规则(Type Const)和(Type Arrow)构造定义良好的类型。
-
规则(Val x)从环境中提取了一个假设并使用它。我们使用符号 Γ ′ , x : A , Γ ′ ′ Γ', x:A, Γ'' Γ′,x:A,Γ′′ 来非正式地表示 x : A x:A x:A 出现在环境的某个地方。
-
规则(Val Fun)将类型 A → B A \to B A→B 赋予某个函数,条件是函数体在形式参数具有类型 A A A 的假设下有类型 B B B。
-
规则(Val Appl)将一个函数应用于一个参数:在验证条件时,相同的类型 A A A 必须出现两次。
ϕ ⊢ ⋄ ( Env ϕ ) \frac{}{\phi \vdash \diamond} (\text{Env}\ \phi) ϕ⊢⋄(Env ϕ)
Γ ⊢ A x ∉ d o m ( Γ ) Γ , x : A ⊢ ⋄ ( Env x ) \frac{\Gamma \vdash A \qquad x \notin dom(\Gamma)}{\Gamma, x:A \vdash \diamond}(\text{Env}\ x) Γ,x:A⊢⋄Γ⊢Ax∈/dom(Γ)(Env x)
Γ ⊢ ⋄ K ∈ B a s i c Γ ⊢ K ( Type Const ) \frac{\Gamma \vdash \diamond \qquad K \in Basic}{\Gamma \vdash K}(\text{Type Const}) Γ⊢KΓ⊢⋄K∈Basic(Type Const)
Γ ⊢ A Γ ⊢ B Γ ⊢ A → B ( Type Arrow ) \frac{\Gamma \vdash A \qquad \Gamma \vdash B}{\Gamma \vdash A \to B}(\text{Type Arrow}) Γ⊢A→BΓ⊢AΓ⊢B(Type Arrow)
Γ ′ , x : A , Γ ′ ′ ⊢ ⋄ Γ ′ , x : A , Γ ′ ′ ⊢ x : A ( Val x ) \frac{\Gamma', x:A, \Gamma'' \vdash \diamond}{\Gamma', x:A, \Gamma'' \vdash x:A}(\text{Val}\ x) Γ′,x:A,Γ′′⊢x:AΓ′,x:A,Γ′′⊢⋄(Val x)
Γ , x : A ⊢ M : B Γ ⊢ λ x : A . M : A → B ( Val Fun ) \frac{\Gamma, x:A \vdash M:B}{\Gamma \vdash \lambda x:A. M: A \to B}(\text{Val Fun}) Γ⊢λx:A.M:A→BΓ,x:A⊢M:B(Val Fun)
Γ ⊢ M : A → B Γ ⊢ N : A Γ ⊢ M N : B ( Val Appl ) \frac{\Gamma \vdash M: A \to B \qquad \Gamma \vdash N:A}{\Gamma \vdash M\ N : B}(\text{Val Appl}) Γ⊢M N:BΓ⊢M:A→BΓ⊢N:A(Val Appl)
下面展示一个相当复杂的推导过程,它使用了 F1 的所有规则。
现在我们已经研究了一个简单的一阶类型系统的基本结构,下面我们将丰富它,使其更接近实际编程语言的类型结构。
我们将为每一个新的类型构造添加一套规则,遵循一个相当有规律的模式。我们从一些基本的数据类型开始:
- U n i t