Skip to content

SECD 抽象机

SECD machine · Stack Environment Control Dump machine

以操作数栈、词法环境、控制指令和调用现场转储执行 applicative expression 的早期抽象机器。

形式陈述

SECD 机器的配置写成四元组

(S,E,C,D),

其中 S 是保存中间值的操作数栈E 是变量到值的词法环境C 是尚待执行的控制指令序列,D 是调用现场的 dump。为避免混合历史文献中的多个变体,本页固定一套带名字变量、栈顶写在左侧的简化指令集:

I::=LDCnLDxLDF(x,Cf)ADDAPRTN.

整数、变量、λ 抽象、左到右应用与加法的编译分别为

[[n]]=[LDCn],[[x]]=[LDx],[[λx.e]]=[LDF(x,[[e]][RTN])],[[e1e2]]=[[e1]][[e2]][AP],[[e1+e2]]=[[e1]][[e2]][ADD].

先编译函数、再编译实参,使本变体采用左到右 call-by-value。值闭包写成 x,Cf,Ef,同时保存形参、函数体代码和定义环境。主要转移是

(S,E,(LDCn)::C,D)(n::S,E,C,D),(S,E,(LDx)::C,D)(E(x)::S,E,C,D),(S,E,LDF(x,Cf)::C,D)(x,Cf,E::S,E,C,D),(n2::n1::S,E,ADD::C,D)((n1+n2)::S,E,C,D),(v::x,Cf,Ef::S,E,[AP]C,D)([],Ef[xv],Cf,S,E,C::D),(v::Sf,Ef,[RTN],S,E,C::D)(v::S,E,C,D).

AP 不在调用者的 S,E,C 上直接执行函数体,而是把三者组成一帧压入 D,清空操作数栈,并从闭包保存的 Ef 扩展参数绑定。函数体最终执行 RTN,从 D 恢复调用现场,再把返回值压回调用者栈。由此,Dump 编码的是被函数调用分隔的剩余控制;它可在现代重述中视为一层续延结构,却不只是硬件返回地址或不带环境的普通调用栈。

闭合项 e([],,[[e]],[]) 开始,([V],E,[],[]) 是终止形。若 vV 把源整数对应到机器整数,并把源函数闭包对应到保存同一词法环境的 SECD 闭包,则对本页纯 CBV 语言,正确性目标可陈述为

eCBVv([],,[[e]],[])SECD([V],E,[],[])且 vV.

证明通常为编译代码建立解码或模拟关系:取指、压栈等行政步骤可对应零个源步骤,AP/RTN 合起来实现一次源函数调用。只有建立这层关系后,才能声称机器实现源语义;机器转移与源 β-归约并非逐步一一对应。

直觉

SECD 把一个递归解释器拆成四盒。Control 告诉机器下一条执行什么,Stack 暂存已经算出的操作数,Environment 解释自由变量,Dump 则在进入函数体前把调用者的三个盒子收好。函数返回时,机器打开最近一帧现场,恢复原控制,再继续完成外层表达式。

Dump 之所以独立,是因为被调用函数拥有自己的操作数栈、定义环境和函数体指令。它保存的不只是“回到哪条指令”,还包括回来后应恢复的值栈与环境。把 D 类比调用栈有助理解 LIFO 次序,但若省略其 S,E,C 内容,就会漏掉 SECD 的控制编码。

例子与边界

完整执行表达式

1+((λx.x+1)2)

。令

Cf=[LDx,LDC1,ADD,RTN],κ=x,Cf,,

初始控制为 [LDC1,LDF(x,Cf),LDC2,AP,ADD]。机器轨迹如下,其中栈顶和 Dump 顶都写在左侧:

([],,[LDC1,LDF(x,Cf),LDC2,AP,ADD],[])([1],,[LDF(x,Cf),LDC2,AP,ADD],[])([κ,1],,[LDC2,AP,ADD],[])([2,κ,1],,[AP,ADD],[])([],{x2},Cf,[[1],,[ADD]])([2],{x2},[LDC1,ADD,RTN],[[1],,[ADD]])([1,2],{x2},[ADD,RTN],[[1],,[ADD]])([3],{x2},[RTN],[[1],,[ADD]])([3,1],,[ADD],[])([4],,[],[]).

AP 时,外层已经算出的 1 和尚待执行的 ADD 一起进入 Dump;RTN 恢复它们,最终加出 4。如果闭包错误地保存调用点环境而不是定义环境,自由变量会按动态作用域解析,轨迹即不再实现词法作用域。

CEK 机器直接以源项为 Control,并把“待算实参”“已有函数”等细粒度工作统一放进 K 帧。这里的 SECD 先编译为指令,普通中间值进入 S,函数边界才把调用者 S,E,C 放入 D。两者都可实现 CBV 与词法闭包,但状态分解和一步粒度不同;CEK 的 K 不能逐字段等同于 SECD 的 D

本变体的 AP 即使位于尾位置也会压入 Dump,因此没有尾调用优化。原始 SECD 的求值次序、环境表示与后续 tail-recursive、callee-save、de Bruijn 变体各有差别;若改动其中一项,必须同步改写编译和正确性关系。加入递归、条件、可变存储、异常或 call/cc 也需要新指令和状态,不能假定四条字母自动覆盖所有语言特性。

推论与应用

SECD 是抽象机器史上的代表实例,展示高阶 applicative expression 如何经显式环境、闭包与控制栈机械执行。它的价值主要在于揭示解释器状态的来源,而不是为现代虚拟机规定唯一布局。

从 SECD 做理性分解,可以把 Dump 识别为调用者续延、把 Control 识别为当前函数内的指令续延,并比较保存/恢复约定。这样的推导说明 CEK、CPS 与 defunctionalization 的结构联系,也解释为何移除 Dump、改变尾调用或合并控制栈后会得到不同机器,而非同一个 SECD 的无关实现细节。历史变体与进一步分解由参考资料承接,本页只固定前述机器,不把版本导航混入状态语义。

参考资料
  • Peter J. Landin, “The Mechanical Evaluation of Expressions,” The Computer Journal 6(4), 1964,pp. 308–320。
  • Olivier Danvy, “A Rational Deconstruction of Landin’s SECD Machine,” BRICS Research Series RS-04-30, 2004。
  • Peter Henderson, Functional Programming: Application and Implementation, Prentice Hall, 1980,SECD-style implementation of applicative languages。