“W中的完整统一器不变量把“最一般”展开为原方程全部解与残余解的因子分解等式,并证明变量消去、构造器冲突与occurs check的两向正确性;变量个数与类型树大小的字典序解释替换膨胀时为何仍…”
“一阶统一是Hindley–Milner 类型推断的约束求解核心:类型构造器扮演函数符号,类型变量扮演一阶变量。Algorithm W在应用处统一函数类型与实参类型,并把得到的替换传播到环境。”
算法Algorithm
施工中 · 内容逐步修订
Algorithm W
完整传播HM替换、区分环境共享与let泛化,并通过嵌套推断账本和MGU解集不变量构造主类型。
固定纯Hindley–Milner语言,项由变量、整数和布尔常量、λ、应用、非递归 let 与二元组构成。单态类型和方案为
“单态”在本页指没有量词,仍允许自由类型变量。环境
对方案,只替换自由变量,并先把量化变量改名到对替换像新鲜的位置。例如
右侧的
它返回的还不只是某个可用类型。在类型变量统一改名后,若某替换
这表达环境约束和结果类型的联合最一般性,等式只要求在原环境的自由变量上比较两替换。特别地,若
fresh 每次返回从未使用过的类型变量;inst 把方案的每个量化变量换成相互不同的新鲜变量,保留其自由变量。unify 返回最一般统一子,失败则整个 W 失败。约定
W(Γ, integer) = (Id, Int)
W(Γ, boolean) = (Id, Bool)
W(Γ, x) = (Id, inst(Γ(x))) // fail if x is unbound
W(Γ, λx.e):
α := fresh()
(S, τ) := W(Γ[x ↦ α], e)
return (S, Sα → τ)
W(Γ, e1 e2):
(S1, τ1) := W(Γ, e1)
(S2, τ2) := W(S1Γ, e2)
β := fresh()
U := unify({ S2τ1 = τ2 → β })
return (U ◦ S2 ◦ S1, Uβ)
W(Γ, (e1,e2)):
(S1, τ1) := W(Γ, e1)
(S2, τ2) := W(S1Γ, e2)
return (S2 ◦ S1, (S2τ1) × τ2)
W(Γ, let x=e1 in e2):
(S1, τ1) := W(Γ, e1)
Γ1 := S1Γ
σ := ∀(FTV(τ1) \ FTV(Γ1)). τ1
(S2, τ2) := W(Γ1[x ↦ σ], e2)
return (S2 ◦ S1, τ2)
返回的
W 沿语法树一边走一边积累“到目前为止必须成立的类型等式”。每遇到未知值就生成新鲜类型变量,每遇到应用就让统一算法校准插头与插座,所得替换立即改写后续世界。let 是唯一的多态关口:先把局部求出的类型中不受环境约束的变量封装成方案,之后每次使用再新鲜实例化。它不是试遍所有类型,而是始终维护最一般解,因此一次运行就得到整个类型族。
对 λf. λx. f x,W 为
let id = λx.x in (id 0, id true),
绑定处得到
边界是省略 occurs check:推断 λx. x x 时会产生
令
考虑闭项
λf.
let id = λx.x in
let y = f (id 0) in
let keep = λz.y in
(id true, keep (id 0))
令 id 的参数类型取新鲜
| 位置 | 新鲜实例或结果变量 | 统一方程 | 本轮 MGU |
|---|---|---|---|
第一次 id 0 |
|||
f (id 0) |
结果 |
||
id true |
|||
第二次 id 0 |
|||
keep (id 0) |
两处 let 的处理发生在表的第二行之后,不能跳过:
y 时,环境已含 y 的类型是 λz.y 时,为 keep 的方案为 最后一行的 y 和 keep 的自由部分。最终累积替换对所有已引入变量的非恒等作用为
这里
取 keep 泛化共享的 y 返回为任意类型;若忘记更新 λ 参数,则结果还会保留不受约束的
对原方程集合
每次取一条当前方程,按以下顺序处理:相同类型删去;同一箭头或积构造器分解成两个分量方程;若一侧为变量
删除、分解和朝向不改变解集。变量消去的关键等式是
从右往左:
终止时
算法终止也有单独度量:先比较残余方程中不同变量的个数,再比较全部类型树节点数。消去变量严格减少第一项,即使替换展开使第二项增加也无妨;删除和构造器分解在第一项不减时严格减少第二项。两项均为自然数,按字典序不可能无限下降。W 的递归调用又只处理原表达式的真子式,每次统一终止,故整个推断过程终止。
对 λx. x x,
再看
λf. let g = f in (g 0, g true)
f 的 g 不能得到
W 把类型方案、一阶统一和HM 推断组合成可实现算法。编译器可据此补全省略的类型、生成错误信息,并在中间表示中插入显式类型信息。
实际实现常用约束生成与求解分离、union–find、等级或区域标记来高效实现泛化;但这些优化必须保持 W 的主类型语义。
成功判断的证明按项结构归纳:变量取方案实例;常量直接定型;λ 用更新后的参数类型;应用对两个归纳结果施加后续替换,再由统一等式接上函数消去;二元组逐分量组合。let 则先在更新环境中泛化不属于环境的变量,再检查其作用域。最一般性还需在这项归纳中同时维护环境因子分解及新鲜实例化义务;MGU不变量解释统一步骤为何不作多余承诺,完整HM主类型定理见文献。[1] 不能只证明统一器输出一个解,就把let的泛化正确性当作已经证明。