Skip to content

定义Definition

类型方案

施工中 · 内容逐步修订

Type scheme · Polytype

通过显式量化类型变量表示一族单态类型的多态对象。

形式陈述 ​

类型方案是表达参数多态的一种方式。先固定通常的 Hindley–Milner 情形,所有量化变量的种类都是值类型 Type,方案形如

σ=∀α1…αn.τ

的多态类型,其中 τ 是单态类型,使用方案时,先把量化变量换成新鲜的单态类型变量,再由该次使用把它们约束为具体单态类型。按类型变量的绑定与自由出现区分,方案的自由类型变量为 FTV(σ)=FTV(τ)∖{α1,…,αn}。在 Hindley–Milner 的类型判断中,类型环境把变量映射到类型方案;读取变量时实例化量化变量,let 绑定时则相对于环境泛化

gen(Γ,τ)=∀(FTV(τ)∖FTV(Γ)).τ.

未被量化的类型变量仍受当前上下文约束,不能任意实例化。

带种类与约束的行方案 ​

记录行系统可在这个外层量化结构上加入种类和约束。这里另取两种种类:Type 表示单态值类型,Row 表示有限、标签不重复的字段行,允许一个尚未确定的行变量作尾部。记录构造 Rec(ρ) 把 Row 变成 Type。相应方案写为

∀(α1:κ1),…,(αn:κn). Q⇒τ,κi∈{Type,Row},τ:Type,

其中 Q 是有限个行约束,例如 ℓ∉ρ 表示字段标签 ℓ 不在该行中。一次实例化对所有量化变量作避免捕获的同时替换,并保持种类:Type 变量换成新鲜值类型变量,Row 变量换成新鲜行变量;同一个绑定变量的所有出现共享同一个新变量。替换同样作用于 Q,其得到的 lacks 义务随实例保留。后续求解必须保持种类且满足这些义务;不能把行变量当作普通值类型变量,也不能在实例化时丢掉条件。

例如

∀(r:Row). name∉r⇒Rec(name:String,r)→Rec(name:String,r)

允许实例 r={age:Int},却拒绝 r={name:Bool},因为后者违反实例化后的 lacks 条件。这正是行多态中保留未知字段的方案形式。

对这份扩展,自由变量还要计入 Q,即从 FV(Q)∪FV(τ) 中删去量化变量,并分别记录其种类。泛化须遵守该行系统的约束生成与求解规则,不能只泛化类型表达式而遗失其约束。只有所有绑定变量都是 Type 且 Q 为空时,才退回前面的普通 HM 方案和实例化规则。

直觉

类型方案不是一个“包含未知量的单类型”,而是一份可反复生成单态实例的模板。每次使用多态变量都要把量化变量换成新的类型变量,否则两次调用会被错误地强迫成同一类型。泛化时排除环境中的自由变量同样关键:这些变量代表外部已经共享的约束,擅自量化会把一份单态资源伪装成多态。类型方案位于可推断性与表达力的折中点,只提供外层全称量化,却足以覆盖大量泛型代码。

例子与边界

恒等函数的方案是 ∀α.α→α。在 let id = λx.x in (id 0, id true) 中,两次读取 id 分别实例化为 Z→Z 与 B→B,因此整个表达式良类型。

边界是λ绑定参数:标准Hindley–Milner不会把 λf.(f 0, f true) 中的f自动视为多态方案,因而该项不可定型。高秩检查可在参数上显式保留∀,但须用刚性变量验证承诺;多态递归则要求递归体能看到完整方案,而非共享单态占位符。二者改变的是方案可以出现和实例化的位置,不是省略一次普通合一步骤。

推论与应用

类型方案是Hindley–Milner 类型推断中 let 多态的表示,Algorithm W在变量处实例化、在 let 处泛化。一阶统一只求解生成的单态约束,量化边界由方案机制管理。

在实现上,方案使泛型函数无需复制源代码即可安全复用;带可变状态的语言还需值限制,避免把具有分配效果的表达式不安全地泛化。

W的嵌套let账本让共享与泛化分别出现:由外层函数得到的结果类型仍自由出现在环境中,所以局部值不能泛化;忽略参数的函数只泛化新参数类型。该页同时给出方案替换避免捕获的具体例子。

参考资料
关系图谱16 个相邻概念 · 1 类关系

拖动节点调整位置。

显示关系

显示:依赖

  1. 前置三跳
  2. 前置二跳
  3. 前置一跳
  4. 当前条目
  5. 后续一跳
  6. 后续二跳
  7. 后续三跳
文字版关系按与当前条目的最短距离分组