“STLC 是静态语义与动态语义相互作用的最小实验场。进展与保持定理说明良类型闭项不会因类型错误卡住,强正规化定理另外证明每条完整 β 归约路径都有限。后者严格强于某个求值策略终止。”
形式陈述 ​
先固定一个足够小、又能暴露证明结构的语言:带布尔值的简单类型 λ 演算。类型、项和值分别为
采用按值调用的小步操作语义。应用先归约函数位置,再归约实参;当二者分别成为 λ 抽象和值时执行 β 步
条件式先归约守卫,并用
选择分支。类型规则包括变量、布尔常量,以及三条核心规则
对这套明确的语法、值定义和求值策略,类型安全分成两条定理:
- 进展(progress):若
,则 ,或者存在 使 ; - 保持(preservation):若
且 ,则 。
于是,如果
证明部件与归纳结构 ​
证明进展前先建立规范形式引理:若闭值 true 或 false;若闭值
进展对类型推导作结构归纳。变量情形在空上下文中不可能出现;抽象与布尔常量已经是值。对应用 true 或 false。
保持需要替换引理:若
随后对一步求值推导作第二次归纳。上下文规则由类型推导的逆置和归纳假设恢复;真正改变语法的 β 情形由
经替换引理推出
直觉
进展检查的是执行路径的“前沿”:一个当前仍受类型系统认可的闭项,要么已经抵达语言承认的结果,要么至少有下一步。保持检查的是路径的“护栏”:执行一步不会把项送出原来的类型集合。沿轨迹看,保持先把类型证书交给下一个状态,进展再保证这个新状态不会意外卡死;两者交替,才能把局部证明延伸到任意有限步。
只证明进展不够,因为一项可以顺利启动,却在一次归约后变成无规则可走的坏项。只证明保持也不够:如果语义错误地不给任何项转移,那么“每次转移都保持类型”会因根本没有转移而平凡成立。类型安全来自静态规则与动态规则逐构造对齐,而不是来自“有类型”这三个字本身。
规范形式引理是这次对齐的铰链。类型推导只告诉我们函数位置具有
例子与边界
项 true false 既不是值,也没有函数应用规则可走;类型系统会拒绝它,因为 true 不可能取得
具有 Bool 类型。它先作 β 步得到 if true then false else true,再得到 false。第一步的类型保持正是替换引理的实例,第二步由条件式两个分支同型保证;每个中间项也都满足进展,直至最终值。
进展定理必须限制为闭项。开项 blame 或进程等待视为合法结果,就必须把它们写进定理;若不希望它们出现,则类型或效果规则还要证明更强性质。
进展与保持不推出终止。纯 STLC 的确强规范化,但那需要可归约性或逻辑关系等另一套证明;不能把这个更强结论偷塞进类型安全推论。给语言加入良类型固定点后,fix (λx:T.x) 可以永远继续,同时每一步都保持
有引用时,运行对象变成项与存储的配置
推论与应用
保持定理可看作“良类型配置集合对一步转移封闭”的归纳不变式,进展则排除该集合中非最终、无后继的状态。这个视角适合机械化:把类型判断、无捕获替换和小步语义编码进证明助手后,每加入一条动态规则,都必须交代它落在哪条类型规则之下;遗漏的规范形式、存储引理或绑定变量条件会变成具体未完成的证明目标。
编译器正确性还需要把这套性质跨越中间表示保存下来。源语言类型安全只说明源语义不进入指定坏状态;优化若改变求值顺序、表示或异常行为,仍需语义保持或模拟证明。字节码验证、typed assembly language 与 proof-carrying code 都复用“静态不变量对机器步封闭”的骨架,但它们的值、堆和控制栈远比 STLC 丰富。
逻辑关系、上下文等价、参数性和信息流安全回答的是更强问题:两个程序是否在所有上下文中行为一致、抽象类型是否被尊重、秘密是否影响公开观察。进展与保持是这些论证的地基,因为它们先保证观察过程本身不会落入未定义的类型错误;它们却不替代任何一项更强的行为规格。
参考资料
- Andrew K. Wright and Matthias Felleisen, “A Syntactic Approach to Type Soundness,” Information and Computation 115(1), 1994, pp. 38–94。
- Benjamin C. Pierce, Types and Programming Languages, MIT Press, 2002, §8.3。
- Benjamin C. Pierce et al., Software Foundations, Volume 2: Programming Language Foundations, 2026 current edition, “Properties of STLC”.