“现代语言中的同名 call/cc 还可能受提示符边界限制,或对跨线程调用另有规定。本文讨论的是给定顶层以内的完整续延,不将所有同名 API 视为相同。要只保存某个显式边界以内的工作,并在完成…”
形式陈述
reset 决定捕获到哪里
分界续延只表示某个边界以内的剩余计算。本页固定经典的 shift/reset:纯粹、从左到右按值、单种无标签提示符,整数、λ、应用与加法。reset(e) 划定边界;shift k.e 将到最近 reset 为止的续延绑定给 k,并在该边界内转而执行 e。
将不跨越 reset 的纯求值上下文写为
E 中没有 reset(E) 分支,因而向外寻找捕获范围时遇到 reset 就停止。外层上下文 F 可以包含其他 reset。核心规则为
x 取新鲜变量,替换使用无捕获替换。另外
本文要求实际存在一个外围 reset。没有外围 reset 的 shift 是未处理控制操作,不给它一个隐含捕获终点;原文的某些顶层求值定义默认一层外部边界,阅读两者时须对齐这个约定。模型没有可变 store、异常、dynamic-wind、多标签提示符或一次性资源。
直觉
保存一段工作,完成后仍回来
完整续延像“把结果交给一整条后续路线,并离开现在的位置”;分界续延像“执行一段保存的子计算,做完再回来”。reset 把子计算的边界写在程序里,shift 决定在这个边界内把哪一段剩余工作变成值。
重新安装 reset 不是装饰。若保存的片段以后又执行 shift,新 shift 应只看见恢复片段自身的边界,而不能把调用 k 的外层工作也捕获进去。规则中的内层 reset 正是保障这个条件的地方。
例子与边界
相同的加法外观,得到 106
计算
reset(1 + shift k. (100 + k(5)))
捕获
调用 k 的结果 6 回到 100+[],这层加法没有被丢弃。对应的call/cc 例 1+callcc(lambda k.100+k(5)) 则得到 6。差别在调用保存控制值时是否保留调用处,而不只是捕获帧数的多少。
一段片段调用两次
reset(1 + shift k. (k(10) + k(20)))
两次调用保存的
多次恢复会多次执行片段里的计算。加入状态后,两次执行可能读写同一存储,不会因名字叫“同一个续延”而自动复用第一次的数值;缓存结果和保存控制片段是两种不同操作。
最近边界与重新安装边界
对
reset(100 + reset(1 + shift k. 10))
shift 只捕获内层
另一个例子直接检验重新安装:
reset((shift k. (100 + k(1))) + (shift j. 10))
第一次捕获的片段是
推论与应用
一条可实现的帧边界协议
可以在 CEK 风格的不可变帧链里加入 Mark 帧。执行 reset 时压入 Mark;执行 shift 时收集当前栈到第一个 Mark 之前的帧,将这段作为 Part 值绑定给 k,再保留 Mark 及外层栈执行主体。若搜索不到 Mark,报告未处理 shift。
应用 Part 时,先在当前调用处 K 上放一个新的 Mark,再把保存片段的帧按原顺序接到这个 Mark 上,并把实参作为片段的输入值。片段返回后经过新 Mark,继续原来的 K。这恰好实现
本页下载执行器的帧链操作在捕获时扫描并保存 d 个帧,需 record_events=False 可关闭日志枚举,但不会消除环境复制。其它持久表示可用不同的切分与拼接策略,成本须按实际数据结构分别计算。多次恢复的总时间至少要覆盖各次实际执行,保存环境还可能延长所引用对象的生命期。
代数效应处理器也可截获操作并提供恢复点,但具体深/浅处理、操作名和恢复规则属于不同接口。某些语言可以相互编码,不等于任意 handler 都满足本文 shift/reset 规则。本文也不把去掉重新安装 reset 的 control/prompt 变体混为同一个算子。
迁移任务使用上面的第二次 shift 例:先在正确执行器中记录两次捕获的帧,再构造一个仅移除 Part 调用时新建 Mark 的错误版本,检验结果从 110 变成 10,并指出第二次捕获多收进了哪一帧。另将最近边界例的内层 reset 删除,比较 110 与 10;这次改变发生在源程序,而非错误实现。两项测试分别检查恢复协议和源程序边界,不能互相替代。终点任务列出这些输入与可比较的帧事件。
参考资料
[1] Małgorzata Biernacka、Dariusz Biernacki、Olivier Danvy,An Operational Foundation for Delimited Continuations in the CPS Hierarchy,BRICS RS-04-29,December 2004,§§4.1–4.4,正文 pp.11–17:双层续延求值器、机器及归约语义;p.17 明确用
[2] Olivier Danvy、Andrzej Filinski,Abstracting Control,Lisp and Functional Programming,1990,pp.151–160,DOI 10.1145/91556.91622。shift/reset 的原始来源;具体操作规则以 [1] 的可读作者报告为核验依据。