Skip to content

无捕获替换

Capture-avoiding substitution

用作用域感知的递归代入替换自由变量,同时保持代入项的自由变量不被捕获。

条目类型
定义

形式陈述

λ 项t[x:=s] 表示把 t 中每个自由出现的 x 换成 s,并保持 s 的自由变量仍然自由。按 α-等价类看,它是良定义的递归函数;若直接操作带名字的原始项,则还须固定一个确定的新鲜名选择策略。核心递归子句为

x[x:=s]=s,y[x:=s]=y(yx),(t1t2)[x:=s]=t1[x:=s]t2[x:=s],(λx.t)[x:=s]=λx.t,(λy.t)[x:=s]=λy.t[x:=s](yx, yFV(s)).

yxyFV(s),选择

zVar(t)Var(s){x,y},

先把显示的绑定器α-换名λz.t{yz},再递归替换。新鲜名字的选择可能产生不同原始语法树,但结果彼此 α-等价。

自由变量满足可核对的边界:若 xFV(t),则

FV(t[x:=s])=(FV(t){x})FV(s);

xFV(t),则 t[x:=s]=t。这两种情形不能用同一无条件等式合并,因为未实际代入时不应凭空加入 s 的自由变量。

直觉

替换把一棵带作用域的语法树嵌入另一棵树。代入项中的自由变量携带原来的外部依赖;穿过同名绑定器时,它们不能突然改为引用该绑定器。遇到冲突先改绑定器名字,正是在移动子树时保留绑定连线

同名绑定器还形成一道停止边界。进入 λx.t 时,体内的 x 已由新声明接管,外部针对 x 的替换不能越过它。这与避免捕获是两项不同检查:前者保护目标项原有绑定,后者保护代入项的自由变量。

无捕获替换示意图
例子与边界

计算 (λy.x)[x:=y] 时,文本替换得到 λy.y,使代入的自由变量 y 被捕获。先改绑定变量,才有

(λz.x)[x:=y]=λz.y.

遮蔽给出另一结果:

(λx.xy)[x:=s]=λx.xy,

因为体内的 x 由该 λ 绑定,而 y 与替换目标不同。相比之下,(xx)[x:=s]=ss 会复制代入项;替换因此可能增大语法树,不能用“项大小必下降”证明 β-归约终止。

替换组合也需要侧条件。若 xyxFV(u),则

t[x:=s][y:=u]αt[y:=u][x:=s[y:=u]].

缺少新鲜性条件时,两边可能对自由变量作不同处理。该公式是顺序代入、β-归约交换和许多归纳证明的常用工具。

推论与应用

无捕获替换直接定义β-归约,并出现在量词实例化、宏展开和函数内联中。类型安全证明使用类型替换引理:

Γ,x:At:B,Γs:AΓt[x:=s]:B.

上下文的弱化条件、变量新鲜性和绑定器分支必须在证明中明确处理;它不是从替换的记号自动得到的结论。

实际实现常用 de Bruijn 索引、显式替换演算或 locally nameless 表示,把选新鲜名字的义务转成索引提升、显式代换规则或有限支持运算。表示改变后仍须证明实现与名字式无捕获替换一致。

参考资料
  • Robert Harper, Practical Foundations for Programming Languages, 2nd ed., Cambridge University Press, 2016, Chapters 1–4。
  • Benjamin C. Pierce, Types and Programming Languages, MIT Press, 2002, Chapters 5–6。
  • Henk Barendregt, The Lambda Calculus: Its Syntax and Semantics, revised ed., North-Holland, 1984, §2.1。
关系图谱17 个相邻概念 · 2 类关系

拖动节点调整位置。

显示关系

显示:依赖

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