Skip to content

环境

Environment · Lexical environment · Evaluation environment

在某个程序位置把标识符解析为值、存储位置或类型信息的有限映射。

形式陈述

环境通常记为 ρΓ,是从标识符到某类语义对象的有限部分映射。求值环境可写成 ρ:VarVal,把变量映到值;带可变状态的语义常令 ρ:VarLoc,再由存储 σ:LocVal 保存位置中的当前值;类型环境则把变量映到类型或类型方案。环境的余域由所讨论的语义决定,不能统一假定为数值。

扩展 ρ[xa] 表示一个新环境,满足

(ρ[xa])(y)={a,y=x,ρ(y),yx.

因此扩展会遮蔽 x 的旧绑定,但不删除外层环境本身;离开内层作用域后仍可回到旧环境。查找只对当前环境定义域中的标识符有结果,未绑定变量应由语义规则明确报错或保持为自由变量。

直觉

环境把“这个名字在这里代表什么”从语法之外的默契变成显式对象。源代码里可以在不同嵌套层重复使用同一个名字,求值器只要沿当前环境查找,就能找到最近且未被遮蔽的绑定。这种表示也让作用域切换成为普通数据操作:进入 let 或函数体时扩展环境,离开时恢复原环境,而不是在整段程序文本中替换名字。

词法作用域的关键是环境由定义位置决定。函数体中的自由变量应沿函数定义时保存的环境解析;调用点只提供实参与新的局部绑定。当前调用栈可以帮助实现环境,但二者不是同一概念:栈记录动态控制过程,词法环境表达静态嵌套所确定的名字关系。

例子与边界

考虑 let x = 1 in let x = 2 in x + 3。求值外层绑定后得到 ρ1=ρ0[x1];进入内层得到 ρ2=ρ1[x2]。表达式 x + 3ρ2 中查到 2,结果为 5。内层结束后恢复 ρ1,此时同一名字再次表示 1。这个过程没有修改旧环境,遮蔽只是查询优先级改变。

环境与 store 的区别在可变变量处最清楚。若 ρ(x)=σ()=1,赋值 x := 2 更新的是 σ[2],环境中 x 到位置 的绑定不变。这样,两个变量可以映到同一位置形成别名,闭包也可共享同一可变单元;若把环境直接写成变量到当前值,就会丢失这层共享行为。

类型环境 Γ 中的条目可能是类型方案而非运行时值,算法 W 在查找后还会实例化量化变量。把求值环境、存储与类型环境都称作 environment 是术语复用,不表示它们的余域和操作规则相同。

推论与应用

操作语义可用判断式 ρev 表示表达式在环境中求值;指称语义则把表达式解释写成 [[e]]ρ程序语言闭包把函数代码与定义时环境配对,使函数离开原作用域后仍能解析自由变量。类型推导里的上下文扩展沿用相同结构,但保存的是静态类型信息。

实际实现可以把环境做成父指针链、持久映射、栈帧或编译后的词法地址。只要查找、遮蔽和作用域恢复的可观察行为一致,这些表示都实现同一语义角色;环境裁剪还可只保留后续确实会访问的自由变量绑定。

参考资料
  • Michael R. Clarkson et al., OCaml Programming: Correct + Efficient + Beautiful, Cornell CS 3110 textbook,the environment model and lexical scope。
  • Benjamin C. Pierce, Types and Programming Languages, MIT Press, 2002,Chs. 9–11 and 22,evaluation and typing contexts。