MathLabs

数学基础

类型论

为数学与计算奠基的理论,其中每个项都带有类型,应用于证明辅助系统。

直观每个值都带有标签

在大多数编程语言中,33 是 `int`,`"hello"` 是 `string`——编译器会在程序运行之前就拒绝 `3 + "hello"`。类型论把这个日常想法变成整个数学的基础:不从集合与属于关系(x∈Ax \in A)出发,而是从项与类型(t:At : A,"tt 具有类型 AA")出发。值得注意的是,一旦类型足够丰富,一个类型就能编码一个数学命题,而该类型的一个项就成为一个证明——程序与证明成了同一种对象。

类型推导的树状图,每个节点带有类型判断。
类型推导表示为一棵树:每个节点是一个判断 Γ⊢t:A\Gamma \vdash t : A,每条边是把项与其子项相连的类型规则。

大学简单类型化λ演算

定义: 类型判断

类型判断 Γ⊢t:A\Gamma \vdash t : A 表示"在语境 Γ\Gamma(变量-类型对的列表 x1:A1,…,xn:Anx_1:A_1,\dots,x_n:A_n)下,项 tt 具有类型 AA"。核心规则:变量具有语境为其指定的类型;抽象 λx:A. t\lambda x{:}A.\,t 在语境扩充 x:Ax:A 后使 tt 具有类型 BB 时,具有类型 A→BA\to B;应用 f uf\,u 在 f:A→Bf:A\to B 且 u:Au:A 时具有类型 BB。

Γ,x:A⊢t:BΓ⊢λx:A. t:A→BΓ⊢f:A→BΓ⊢u:AΓ⊢f u:B\dfrac{\Gamma, x{:}A \vdash t : B}{\Gamma \vdash \lambda x{:}A.\,t : A\to B} \qquad \dfrac{\Gamma \vdash f : A\to B \quad \Gamma \vdash u : A}{\Gamma \vdash f\,u : B}

马丁-洛夫依赖类型论进一步推广了这一点:函数的结果类型可以依赖于其参数的值。**Π\Pi 类型**(依赖函数类型)∏x:AB(x)\prod_{x:A} B(x) 收集把每个 x:Ax:A 送到类型 B(x)B(x) 的项的函数——当 BB 不提及 xx 时,它退化为普通的 A→BA\to B。对偶地,**Σ\Sigma 类型**(依赖对类型)∑x:AB(x)\sum_{x:A} B(x) 收集满足 a:Aa:A、b:B(a)b:B(a) 的对 ⟨a,b⟩\langle a,b\rangle——其第二分量的类型依赖于第一分量;当 BB 不提及 xx 时,它退化为普通积 A×BA\times B。

∏x:AB(x),∑x:AB(x)\prod_{x:A} B(x), \qquad \sum_{x:A} B(x)
依赖类型及其Curry–Howard解读
类型构造子非依赖特殊情形Curry–Howard解读
∏x:AB(x)\prod_{x:A} B(x)A→BA\to B(当 BB 为常量时)∀x:A, B(x)\forall x{:}A,\ B(x)
∑x:AB(x)\sum_{x:A} B(x)A×BA\times B(当 BB 为常量时)∃x:A, B(x)\exists x{:}A,\ B(x)
单值公理:(A≃B)≃(A=UB)(A\simeq B) \simeq (A =_{\mathcal U} B)—等价的类型被等同

在简单类型化λ演算中,若 Γ⊢t:A\Gamma \vdash t : A,则 tt 是强正规化的:从 tt 出发的每一个 β\beta 归约序列都会在有限步内终止。

为什么成立?

这正是简单类型化λ演算尽管有函数应用和类似递归的嵌套,却不是图灵完全的原因——任何良类型的项都不可能永远循环,这正是该片段中的类型检查器总能停机的原因。

证明

(泰特的可归约性方法。)对类型的结构做归纳,为每个类型 AA 定义"可归约项"的集合 REDA\mathrm{RED}_A:对基本类型 oo,令 REDo\mathrm{RED}_o 为所有强正规化项组成的集合 SNSN;对函数类型,令 REDA→B={t:for every u∈REDA, t u∈REDB}\mathrm{RED}_{A\to B} = \{t : \text{for every } u \in \mathrm{RED}_A,\ t\,u \in \mathrm{RED}_B\}。

首先对 AA 做归纳,一并证明三条技术性质:(CR1) REDA\mathrm{RED}_A 中每个项都是强正规化的;(CR2) REDA\mathrm{RED}_A 对 β\beta 归约封闭(若 t∈REDAt\in\mathrm{RED}_A 且 t→t′t\to t',则 t′∈REDAt'\in\mathrm{RED}_A);(CR3) 任何"中性"项(变量或应用,而非抽象),若其所有一步归约结果都已在 REDA\mathrm{RED}_A 中,则它本身也在 REDA\mathrm{RED}_A 中。基本情形由 SNSN 的定义直接得出;函数类型情形展开 REDA→B\mathrm{RED}_{A\to B} 的定义,并对更小的类型 AA、BB 使用归纳假设。

接着证明,在给自由变量代入可归约项后,每个良类型的项都是可归约的:对 Γ⊢t:A\Gamma \vdash t:A 的类型推导做归纳,若 σ\sigma 给 Γ\Gamma 中每个变量 xx 指派一个项 σ(x)∈REDΓ(x)\sigma(x)\in\mathrm{RED}_{\Gamma(x)},则 tσ∈REDAt\sigma \in \mathrm{RED}_A。变量情形显然。应用情形直接由 REDA→B\mathrm{RED}_{A\to B} 的定义得出。抽象情形最需要小心:把 (λx. t)σ(\lambda x.\,t)\sigma 作用于任意 u∈REDAu\in\mathrm{RED}_A,该项经一步 β\beta 归约为 t(σ,x:=u)t(\sigma,x{:=}u),由归纳假设(应用于扩充后的代入)可知它属于 REDB\mathrm{RED}_B;性质(CR3)——对归约结果已全部可归约的项的"展开"封闭性——由此把 (λx. t)σ u(\lambda x.\,t)\sigma\,u 本身也纳入 REDB\mathrm{RED}_B,于是按定义 (λx. t)σ∈REDA→B(\lambda x.\,t)\sigma \in \mathrm{RED}_{A\to B}。

最后,把这应用于恒等代入:每个变量 x:Ax{:}A 本身就是中性的且完全没有归约结果,故由 (CR3) 它平凡地属于 REDA\mathrm{RED}_A。于是对任意闭推导 Γ⊢t:A\Gamma \vdash t:A,取 σ\sigma 把每个 xx 映到自身,得到 t=tσ∈REDAt = t\sigma \in \mathrm{RED}_A。由 (CR1),REDA⊆SN\mathrm{RED}_A \subseteq SN,故 t∈SNt \in SN:每个良类型的项都是强正规化的。■\blacksquare

进阶Curry–Howard对应

把 A→BA\to B 读作"若 AA 则 BB",把 A×BA\times B 读作"AA 且 BB",一个命题的证明就成为对应类型的良类型程序——这就是命题即类型,证明即程序。检验证明归结为对一个项做类型检查;构造证明归结为写出正确类型的程序。

设 φ\varphi 是由原子、∧\wedge 与 →\to 构成的命题公式,设 AφA_\varphi 是将原子翻译为基本类型、∧\wedge 翻译为积 ×\times、→\to 翻译为函数类型 →\to 所得到的简单类型。那么 φ\varphi 在直觉主义命题逻辑中可证,当且仅当 AφA_\varphi 被居留:存在闭项 tt 满足 ⊢t:Aφ\vdash t : A_\varphi。

为什么成立?

这正是"证明助手不过是一个类型检查器"这句话背后的精确论断:Coq、Agda、Lean 接受一个定理的机器检验证明,恰恰是在它们接受对应类型的一个项时;而类型检查(不同于一般的定理证明)是一个小巧、快速、机械且高度可信的算法。

证明

(证明变为程序。)对 ⊢φ\vdash \varphi 的自然演绎推导做归纳。若最后一条规则是 →\to-引入,由假设 ψ\psi 下 χ\chi 的子证明推出 φ=ψ→χ\varphi=\psi\to\chi:由归纳假设,该子证明被翻译为项 tt,满足 x:Aψ⊢t:Aχx{:}A_\psi \vdash t : A_\chi(假设变为自由变量 xx);于是 λx:Aψ. t\lambda x{:}A_\psi.\,t 是闭的,且类型为 Aψ→Aχ=AφA_\psi\to A_\chi=A_\varphi。若最后一条规则是由 ψ→χ\psi\to\chi 与 ψ\psi 得到的 →\to-消去(分离规则):由归纳假设得到项 t1:Aψ→Aχt_1 : A_\psi\to A_\chi 与 t2:Aψt_2 : A_\psi;于是 t1 t2:Aχt_1\,t_2 : A_\chi。∧\wedge-引入与 ∧\wedge-消去规则对称地翻译为配对 ⟨t1,t2⟩\langle t_1,t_2\rangle 与两个投影。证明系统的每条规则都有对应的项构造规则,因此对推导做归纳就能产生类型为 AφA_\varphi 的良类型闭项。

(程序变为证明。)反过来,对类型推导 ⊢t:A\vdash t : A 做归纳。每条类型规则——变量、抽象、应用、配对、投影——与某条自然演绎规则——假设、→\to-引入、→\to-消去、∧\wedge-引入、∧\wedge-消去——具有完全相同的树形结构。于是取同一棵推导树,擦除所有项,并把每个类型读作它所翻译自的公式:这恰好就是 φ\varphi 的一个合法的直觉主义证明(其中 A=AφA=A_\varphi)。

由于这两个翻译(证明 →\to 项,以及项 →\to 证明)在推导树上互为结构逆——一个逐规则地精确撤销另一个所做的——φ\varphi 可证恰好当 AφA_\varphi 被居留时成立。■\blacksquare

大学实际应用与典型例题

丰富的类型系统能在程序运行之前捕获整整一类错误:Rust 的所有权类型在编译期防止释放后使用和数据竞争,依赖类型语言能把"这个列表恰有 nn 个元素"或"这个下标在范围内"之类的不变量直接编码进类型,使某些运行时错误变得不可表示。最深层的应用就在证明助手内部:Coq、Agda、Lean 都拥有一个小巧、经过仔细审计的内核,只做一件事——对项做类型检查——由于 Curry–Howard 对应,接受一个定理类型的项就是接受一个经机器检验的证明,因此信任一个像费特-汤普森定理或四色定理那样规模的数学结果,就归结为信任区区几百行类型检查代码,而不是构建证明项所用的(规模大得多、审计难得多的)策略代码。

例题: 普通函数类型无法表达的 Π\Pi 类型

设 Vec A n\mathrm{Vec}\,A\,n 为元素类型为 AA 的长度为 nn 的列表类型。写出一个居留 ∏n:N(Vec A n→Vec A n)\prod_{n:\mathbb N} (\mathrm{Vec}\,A\,n \to \mathrm{Vec}\,A\,n) 的项——"对每个长度 nn,给出长度为 nn 的向量上的一个函数"——并解释为何简单类型(没有 Π\Pi)完全无法表达这个规范。

解答

项 λn:N. λv:Vec A n. v\lambda n{:}\mathbb N.\,\lambda v{:}\mathrm{Vec}\,A\,n.\,v 是可行的:对每个自然数 nn,它接受一个长度为 nn 的向量 vv 并原样返回——显然是良类型的,因为无论提供哪个 nn,v:Vec A nv : \mathrm{Vec}\,A\,n 都以类型 Vec A n\mathrm{Vec}\,A\,n 被返回。

使这成为 Π\Pi 的真正应用而非普通函数类型的原因在于,第二个参数的类型(Vec A n\mathrm{Vec}\,A\,n)提及了第一个参数(nn)的值。在简单类型化λ演算中,函数的参数类型与结果类型一旦确定便永远固定——A→BA\to B 中的 A,BA,B 在知道类型 AA 的任何具体值之前就已选定。无法只用 →\to 写出"给我一个自然数 nn,并根据你给的数,我将要求恰好那个长度的向量":你需要某个单一的固定类型 Vec A n\mathrm{Vec}\,A\,n 同时适用于所有 nn,而这并不是 Vec\mathrm{Vec} 的含义。

具体来说,若尝试用普通函数类型来写,最多只能得到针对某个固定占位长度的 N→(Vec A ?→Vec A ?)\mathbb N \to (\mathrm{Vec}\,A\,? \to \mathrm{Vec}\,A\,?)——毫无用处,因为它会拒绝任何其他长度的向量,或者接受长度错误的向量。Π\Pi 类型 ∏n:N(⋯ )\prod_{n:\mathbb N}(\cdots) 正是能让值域"回头看"所收到的 nn 的具体值的类型构造子,这正是简单类型无法表达的那种依赖性。

例题: 函数复合的Curry–Howard对应

公式 (A→B)→((B→C)→(A→C))(A\to B)\to((B\to C)\to(A\to C)) 表达了蕴含的传递性。根据Curry–Howard,找出证明它的 λ\lambda 项,并逐步验证其类型推导。

解答

该项为 t=λf:A→B. λg:B→C. λx:A. g (f x)t = \lambda f{:}A\to B.\,\lambda g{:}B\to C.\,\lambda x{:}A.\,g\,(f\,x)——正是作用于 xx 的函数复合 g∘fg\circ f。

从内到外推导类型。在语境 f:A→B, g:B→C, x:Af{:}A\to B,\,g{:}B\to C,\,x{:}A 中:由于 f:A→Bf:A\to B 且 x:Ax:A,应用得 f x:Bf\,x:B。由于 g:B→Cg:B\to C 且 f x:Bf\,x:B,应用得 g (f x):Cg\,(f\,x):C。

现在从内到外解除各抽象。λx:A. g (f x)\lambda x{:}A.\,g\,(f\,x) 在语境 f:A→B, g:B→Cf{:}A\to B,\,g{:}B\to C 下具有类型 A→CA\to C。接着 λg:B→C. (⋯ )\lambda g{:}B\to C.\,(\cdots) 在语境 f:A→Bf{:}A\to B 下具有类型 (B→C)→(A→C)(B\to C)\to(A\to C)。最后 λf:A→B. (⋯ )\lambda f{:}A\to B.\,(\cdots) 不再有任何自由假设,具有类型 (A→B)→((B→C)→(A→C))(A\to B)\to((B\to C)\to(A\to C))——一个闭项。

于是 ⊢t:(A→B)→((B→C)→(A→C))\vdash t : (A\to B)\to((B\to C)\to(A\to C)),即 tt 是传递性的一个Curry–Howard证明:"若 AA 蕴含 BB,且 BB 蕴含 CC,则 AA 蕴含 CC"——这与函数式程序员为复合两个函数所写的项完全相同。

研究当前研究

在简单类型化λ演算中,若 f:A→Bf : A \to B 且 a:Aa : A,那么 f af\,a 的类型是什么?

像Coq、Agda、Lean这样的证明助手,为何能信任一个长达数千行、经机器检验的证明?

当 B(x)B(x) 实际上不依赖于 xx 时,∏x:AB(x)\prod_{x:A} B(x) 退化为什么?

单值公理 (A≃B)≃(A=UB)(A\simeq B) \simeq (A =_{\mathcal U} B) 表明……

参考文献

  1. The Univalent Foundations Program (2013). Homotopy Type Theory: Univalent Foundations of Mathematics
  2. Wikipedia contributors (2024). Curry–Howard correspondence
  3. Wikipedia contributors (2024). Intuitionistic type theory