Bend 如何用 scopeless lambda 实现 callcc 控制流?
2026/9/14 14:54:32 网站建设 项目流程

Bend 如何用 scopeless lambda 实现 callcc 控制流?

【免费下载链接】BendA massively parallel, high-level programming language项目地址: https://gitcode.com/GitHub_Trending/be/Bend

Bend 并没有把 call/cc(call with current continuation,调用当前 continuation)做成语言内置原语,但文档明确指出它可以用 scopeless lambda(无作用域 lambda)实现,仓库里还提供了一个可直接运行的示例 examples/callcc.bend。本文基于 docs/using-scopeless-lambdas.md 和该示例,讲清楚三件事:scopeless lambda 的绑定规则、callcc组合子的写法,以及如何运行并核对输出结果。

scopeless lambda 的绑定规则

scopeless lambda 是 HVM 对 lambda 项内部表示带来的一个特性:lambda 的变量名前加一个美元符号$,它绑定的变量就可以在这个 lambda 的函数体之外使用。

λ$x $x # 写成 scopeless 形式的恒等函数

在 imp 语法里,同样的东西写作lambda $x: ...。文档给的最小例子是:

main = (((λ$x 1) 2), $x) # $x gets replaced by 2 and the application ((λ$x 1) 2) gets replaced by 1 # Outputs (1, 2)

调用λ$x 1时,$x被设为2;而λ$x 1本身求值为1,所以程序输出(1, 2)。这是理解后面callcc的关键:被调用一次的 scopeless lambda 会给绑定变量"赋值",而这个变量在调用点之外仍然可读。

写代码前需要知道三条限制,它们直接决定实现方式:

  1. 绑定不跨定义。bound 变量只对当前 term 有效:

    def = $x main = (((λ$x 1) 2), def)

    这种写法是无效的,$x在另一个顶层定义中不可见。

  2. 丢弃 scopeless lambda 会产生 ERA。如果把λ$x 1直接丢弃,$x会被擦除,读出来的是*(ERA/eraser,HVM 表示值被擦除的特殊 term):

    main = let _ = λ$x 1 # Discard and erase the scopeless lambda (2, $x) # Outputs (2, *)
  3. 一个 scopeless lambda 被调用两次会产生叠加(superposition)。例如把f = λ$x 1分别用23调用后读$x,结果是{2 3}——两个值的叠加,且叠加里参数的顺序由编译器决定,不保证稳定。文档的结论是:如果需要确定顺序,要显式地做 duplication,不能依赖隐式复制的顺序。

这三条规则解释了为什么callcc的实现里必须小心处理"continuation 被填入后留下的垃圾值"——它最终要作为被丢弃的 scopeless lambda 绑定来处理。

写 callcc 组合子

examples/callcc.bend 的完整代码如下:

# This program is an example that shows how scopeless lambdas can be used. Seq (a: A) (b: B) : A = a # Create a program capable of using `callcc` unchecked CC.lang : ((((a -> b) -> b) -> c) -> c) -> d = λprogram let callcc = λcallback (λ$garbage($hole) (callback λ$hole(0))); let result = (program callcc); let garbage = $garbage; # Discard `$garbage`, which is the value returned by `callback` (Seq result garbage) main: u24 = (CC.lang λcallcc # This code calls `callcc`, then calls `k` to fill the hole with `42`. # This means that the call to callcc returns `42`, and the program returns `52` (+ 10 (callcc λk(+ (k 42) 1729))) )

逐块拆解它的运作方式:

  • Seq a b = a丢弃第二个参数。它的作用是把"continuation 的返回值"从结果元组里排挤掉,最终程序只输出a
  • CC.lang是一个 CPS(continuation passing)外壳:它接收一个program,把callcc传给它执行,得到result后返回(Seq result garbage),其中garbage就是$garbage
  • 核心的一行是callcc = λcallback (λ$garbage($hole) (callback λ$hole(0)))callcc收到的callback会被包装成一个 scopeless lambda:当callback在某处被调用时,调用产生的值会填入$hole,从而"替换"掉callcc表达式所在的整个位置;callback自己的返回值则落到$garbage上。
  • main里的(+ 10 (callcc λk (+ (k 42) 1729)))演示了跳转:k 4242填入 hole,于是callcc表达式整体变成42(+ 10 42)得到52;而(+ (k 42) 1729)callback的返回值,作为$garbageSeq丢弃(对应上一条"丢弃 scopeless lambda"的规则,它最终被擦除)。

注意CC.lang上带了unchecked注解:$garbage$hole这类无作用域绑定不在类型检查器的支持范围内(见 docs/type-checking.md 的 Limitations 一节,明确列出不支持 unscoped variables 和 variable binds),所以组合子本身只能跳过类型检查,而main仍然可以标注u24

运行与结果核对

运行前需要按 README.md 的 Install 一节装好两个依赖:

# HVM2 是 Bend 的运行解释器 cargo install hvm hvm --version # 安装 Bend 本身 cargo install bend-lang bend --version

然后把示例保存为callcc.bend(仓库内已有现成文件 examples/callcc.bend,可直接使用),用默认解释器运行:

bend run examples/callcc.bend

如果想换解释器,README 给出的对应命令是:bend run-rs <file.bend>(Rust 解释器,顺序执行)、bend run-c <file.bend>(C 解释器,默认)、bend run-cu <file.bend>(CUDA 解释器,需要 NVIDIA GPU 和 CUDA 12.x 工具链)。

预期输出:

52

这个值就是仓库 golden 测试记录的运行结果:测试 tests/golden_tests.rs 会用run-cexamples/下所有.bend文件并快照输出,tests/snapshots/examples__callcc.bend.snap 中记录的就是52。所以核对方式很直接:命令行输出与快照一致即表示callcc按预期工作——callcc调用点返回了42,加10后程序输出52,而(+ (k 42) 1729)部分被作为垃圾擦除。

边界与限制

  • 类型检查$a形式的无作用域变量和let $a = ...绑定不被类型检查器支持,凡是包含 scopeless lambda 绑定的定义(如本例的CC.lang)需要unchecked注解跳过检查;使用它们的普通入口点(main)仍可正常标注类型。
  • 叠加顺序不保证:如果一个 scopeless lambda 被隐式复制并多次调用,$x读出来的是叠加,且叠加中值的顺序由编译器决定。需要确定顺序时按文档做法显式 duplication。
  • 绑定范围:scopeless 变量只在同一个 term 内有效,跨顶层定义引用$x是无效的,所以callcc这种组合子必须像示例一样内联在单个λ体里构造。

这套写法的要点可以归纳为一句话:scopeless lambda 的$绑定充当了"从任意位置写回 continuation 入口"的通道,callcc只是这条规则加上一个 CPS 外壳(CC.lang)和一个用于丢弃垃圾值的Seq的组合。想继续深入,docs/using-scopeless-lambdas.md 的 Duplicating scopeless lambdas 一节给出了被丢弃和被调用两次时的完整推导,叠加语义的细节在 docs/dups-and-sups.md 中。

【免费下载链接】BendA massively parallel, high-level programming language项目地址: https://gitcode.com/GitHub_Trending/be/Bend

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询