“异常语义连接操作语义、续延和效应系统。把正常与异常路径编译成成功/失败双续延的完整条款见CPS 变换;异常表与栈展开则是另一类常见运行时实现。在代数效应处理器中,异常可表现为 operati…”
形式陈述 ​
代数效应先声明操作签名 perform op(v) 触发操作,处理器写成
若被处理计算返回 op。具体检查算法负责核对所有操作分支、结果类型和效应行,却不定义运行时恢复行为。
令
因此
若当前 handler 没有匹配 op 的 clause,该操作必须连同从原操作点到当前边界的恢复上下文继续向外传播,由更外层 handler 处理。传播规则不能丢掉这段上下文;若操作最终到达顶层仍无人处理,语义应把它列为显式的未处理操作结果或运行错误,而不是让配置无规则可走。
“代数”来自操作及其方程生成自由模型、handler 作为保持这些操作结构的解释或 fold 的观点。它要求先说清操作方程;受限控制、作用域操作和资源生命周期未必都由同一简单代数覆盖。
直觉 ​
perform 不决定操作最终怎么实现,它只是向最近处理器发出一项请求。处理器既看到请求参数,也拿到“如果给请求一个结果,原计算将如何继续”的恢复点。于是同一程序中的操作可以被解释成真实 I/O、纯模拟、收集日志或测试桩,而调用点不必硬编码策略。
return clause 处理没有再触发操作、正常走到终点的计算。若只写 operation clauses,普通返回值就没有进入处理器结果的路径;这也是 handler 不能简化成异常 catch 的原因。
例子与边界 ​
异常是最简单的非恢复例子:raise:E→0 的处理分支忽略 choose:1→Bool 的处理器可分别以 true、false 调用
状态操作 get:1→S、put:S→1 可由 handler 显式携带当前状态:get 把状态交给 put 以新状态继续。这个例子展示把效应重新解释为纯状态传递,但全局状态、动态作用域资源或带析构保证的操作可能需要额外方程、scoped effects 或线性恢复,不能由一句“所有效应都代数化”概括。
若 multi-shot handler 重放含文件句柄、锁或外部 I/O 的续延,效果可能重复,资源也可能被非法复用。one-shot 限制、子结构类型或运行时复制检查是不同解决路线;处理器语法本身不自动保证安全。
推论与应用 ​
代数效应与处理器可统一表达异常、非确定性、生成器、状态解释和可测试的服务接口。编译实现可把 handler 翻译成受限 CPS;正确性要求源 perform/handle 转移由目标续延调用模拟,并保持正常返回、未处理操作与恢复次数等观察。
类型安全通常陈述为保持与进展:良类型转移保持结果类型;若程序没有未处理效应,则闭合良类型计算要么返回值、要么继续运行。若语言允许操作冒出最外层,“未处理操作”应作为显式结果,而不是误判为语义卡住。
参考资料
- Gordon D. Plotkin and Matija Pretnar, “Handlers of Algebraic Effects,” ESOP, 2009。
- Gordon D. Plotkin and John Power, “Algebraic Operations and Generic Effects,” Applied Categorical Structures 11, 2003。
- Andrej Bauer and Matija Pretnar, “Programming with Algebraic Effects and Handlers,” Journal of Logical and Algebraic Methods in Programming 84(1), 2015。