Explicit Typing Rules [blog/lti/explicit]
Explicit Typing Rules [blog/lti/explicit]
在定义了类型与子类型关系之后,我们便可以给出内核语言的类型定则。这些规则定义了类型判断(typing judgment)的形式 $\Gamma \vdash e : T$,意为「在上下文 $\Gamma$ 中,项 $e$ 拥有类型 $T$ 」。
与子类型关系的定义一脉相承,这里的类型定则同样采用了一种算法化的呈现方式。且省略了传统类型系统中常见的包容规则(subsumption,若 $e$ 的类型为 $S$ 且 $S \lt: T$,则 $e$ 的类型亦可为 $T$)。
通过省略此规则,本系统为每一个可被类型化的项,都计算出一个唯一的、最小的类型,作者称之为该项的显式类型(manifest type)。这一设计选择,并不改变语言中可被类型化的项的集合,而只是确保了任何一个项的类型推导路径都是唯一的。这极大地增强了系统的可预测性。
核心定则 (Core Rules)
变量 (Variable)
$$ \frac{}{\Gamma \vdash x : \Gamma(x)} \quad (\text{Var}) $$
抽象 (Abstraction) 此规则统一了传统的项抽象与类型抽象。
$$ \frac{\Gamma, \overline{X}, \overline{x}:\overline{S} \vdash e : T}{\Gamma \vdash \mathbf{fun}[\overline{X}] (\overline{x}:\overline{S}) e : \forall \overline{X}.\overline{S} \to T} \quad (\text{Abs}) $$
若想要求解 $\mathbf{fun}[\overline{X}] (\overline{x}:\overline{S})$ 的类型,必须在上下文 $\Gamma$ 中添加类型变量 $\overline{X}$ 和值变量 $\overline{x}:\overline{S}$ 的绑定。然后,在这个扩展的上下文中,推导函数体 $e$ 的类型为 $T$。最终,整个函数的类型便是 $\forall \overline{X}.\overline{S} \to T$。
应用 (Application) 此规则同样统一了传统的项应用与多态应用。它首先推导函数
f
的类型,然后验证所有实际参数(包括类型参数与项参数)是否与函数的签名相符。 这里的要求更宽松了一些:只要实际参数的类型满足参数类型的子类型关系即可,而不需要完全匹配。$$ \frac{\Gamma \vdash f : \forall \overline{X} . \overline{S} \to R \quad \Gamma \vdash \overline{e} \lt: [\overline{T}/\overline{X}]\overline{S}}{\Gamma \vdash f[\overline{T}] (\overline{e}) : [\overline{T}/\overline{X}]R} \quad (\text{App}) $$
其中,记法 $\Gamma \vdash \overline{e} \lt: [\overline{T}/\overline{X}]\overline{S}$ 是一个缩写,表示 $\Gamma \vdash \overline{e} \lt: \overline{U}$,然后验证 $\overline{U} \lt: [\overline{T}/\overline{X}]\overline{S}$。最终,整个应用表达式的结果类型,是通过将实际类型参数 $\overline{T}$ 代入函数原始返回类型 $R$ 中得到的。
1. Type Parameter Substitution [blog/lti/subst_code]
这里出现了一个新的记号 $[\overline{T}/\overline{X}]R$, 它表示将类型参数 $\overline{X}$ 替换为实际类型 $\overline{T}$ 后的结果类型。 这个记号在后续的代码中会频繁出现。 下面的代码定义
mk_subst
函数,用来生成一个类型替换映射, 还有一个apply_subst
函数可以将这个映射应用到一个具体类型上。 $[\overline{T}/\overline{X}]R$ 就是通过 $\text{apply\_subst}(\text{mk\_subst}(\overline{X},\overline{T}), R)$ 得到的。pub fn mk_subst(vars : Array[Var], tys : Array[Type]) -> Map[Var, Type] raise { guard vars.length() == tys.length() else { raise ArgumentLengthMismatch } Map::from_array(vars.zip(tys)) } pub fn apply_subst(self : Type, subst : Map[Var, Type]) -> Type { match self { TyVar(v) => subst.get(v).unwrap_or(self) TyTop | TyBot => self TyFun(xs, ss, t) => { let fs = subst.iter().filter(kv => !xs.contains(kv.0)) |> Map::from_iter let new_ss = ss.map(s => s.apply_subst(fs)) let new_t = t.apply_subst(fs) TyFun(xs, new_ss, new_t) } } }
Bot 应用 (Bot Application) $\bot$ 类型的引入,要求我们补充一条特殊的应用规则,以维护系统的类型安全(type soundness)。 由于 $\bot$ 是任何函数类型的子类型,一个类型为 $\bot$ 的表达式应当可以被应用于任何合法的参数,而不会产生类型错误。
$$ \frac{\Gamma \vdash f : \bot \quad \Gamma \vdash \overline{e} : \overline{S}}{\Gamma \vdash f[\overline{T}] (\overline{e}) : \bot} \quad (\text{App-Bot}) $$
此规则规定,当一个类型为 $\bot$ 的项被应用时,无论参数为何,整个表达式的结果类型也是 $\bot$,这正是我们能给出的最精确(即最小)的结果类型。
这些规则共同保证了本类型系统的一个关键性质,即显式类型的唯一性(Uniqueness of Manifest Types):若 $\Gamma \vdash e : S$ 且 $\Gamma \vdash e : T$,那么必有 $S=T$。