Roc 语言 List.fold_try 深入解析:从 REPL 快照测试看“遇到第一个 Err 即停止“的折叠语义

发布时间:2026/9/19 12:11:23
Roc 语言 List.fold_try 深入解析:从 REPL 快照测试看“遇到第一个 Err 即停止“的折叠语义 Roc 语言 List.fold_try 深入解析从 REPL 快照测试看遇到第一个 Err 即停止的折叠语义【免费下载链接】rocA fast, friendly, functional language.项目地址: https://gitcode.com/GitHub_Trending/ro/rocList.fold_try是 Roc 标准库src/build/roc/Builtin.roc提供的可失败折叠函数它像List.fold一样从左到右遍历列表并逐项更新state但当某一步骤函数返回Err时会立即终止折叠并原样返回该错误而不是继续处理剩余元素。本文以仓库中的 REPL 快照测试 test/snapshots/repl/list_fold_try.md 为骨架结合 Builtin.roc 中的真实实现讲解其类型签名、调用语法、返回值约定、与普通fold的差异以及配套的有副作用变体fold_try!并演示如何用roc repl逐行复现这些行为。读完你不仅能熟练写出正确的fold_try调用还能理解这类短路折叠在错误处理中的典型用法。一、快照文件REPL 行为的可执行说明书先看这份作为本文核心依据的快照文件 test/snapshots/repl/list_fold_try.md它由三个部分组成METAini格式的元信息descriptionList.fold_try folds until the first Err一句话点明了被测语义——折叠直到第一个 Errtyperepl表明这是 REPL 交互快照SOURCE以»为提示符的 REPL 输入行每条都是可直接粘贴进roc repl的表达式OUTPUT与输入一一对应用---分隔的期望输出# PROBLEMS为NIL表示该快照当前无已知问题。快照测试的加载与执行逻辑位于 src/snapshot_tool/main.zig例如其中第 955 行按content.meta.node_type .repl分支处理 REPL 快照roc snapshot --trace-eval 单个_repl_快照.md可用于单条运行。这套机制把行为规范固化成了可回归验证的文本文件。二、四条输入四种视角快照到底验证了什么SOURCE 段共四条语句每条都在验证fold_try的一个侧面» [1, 2, 3].fold_try(0, |sum, n| Ok(sum n)) » [1, 2, 3, 4].fold_try(0, |sum, n| if n 3 { Ok(sum n) } else { Err(Stop) }) » [1.I64, 2, 3].fold_try!(0, |sum, n| Ok(sum n)) » List.fold_try([], 0, |sum, n| Ok(sum n))1. 全成功路径返回Ok(最终状态)» [1, 2, 3].fold_try(0, |sum, n| Ok(sum n)) Ok(6.0)初始state为0逐步累加 1、2、3全程没有失败最终返回Ok(6.0)。注意输出是6.0默认数字字面量是小数fractional这是 Roc REPL 未显式标注类型时的默认数值推断与 test/snapshots/repl/list_fold_simple.md 中[1, 2].fold(0, |acc, n| acc n)输出3.0的观察一致。2. 中途失败短路返回第一个Err» [1, 2, 3, 4].fold_try(0, |sum, n| if n 3 { Ok(sum n) } else { Err(Stop) }) Err(Stop)元素 1、2 通过n 3但当遍历到3时步骤函数返回Err(Stop)折叠立即停止剩余元素4不会被处理最终整体返回Err(Stop)。这正是description中 folds until the first Err 的具体表现。3. 类型标注 副作用变体fold_try!» [1.I64, 2, 3].fold_try!(0, |sum, n| Ok(sum n)) Ok(6)这里用1.I64把列表元素标注为I64因此累加结果是I64输出显示为Ok(6)无小数点。同时它调用的是fold_try!——带!的效果函数effectful变体步骤函数类型为state, item Try(state, err)可在步骤内执行能力capability调用。4. 函数式调用形式 空列表» List.fold_try([], 0, |sum, n| Ok(sum n)) Ok(0.0)两个要点函数式调用List.fold_try(list, initial, step)与管道式list.fold_try(initial, step)等价参数顺序为列表、初始状态、步骤函数空列表边界列表为空时不会调用步骤函数直接返回Ok(初始状态)即Ok(0.0)。三、源码级实现Builtin.roc中的fold_try快照验证的行为正是 src/build/roc/Builtin.roc 中fold_try与fold_try!的实现fold_try : List(item), state, (state, item - Try(state, err)) - Try(state, err) fold_try |list, initial, step| { var $state initial for item in list { $state step($state, item)? } Ok($state) } fold_try! : List(item), state, (state, item Try(state, err)) Try(state, err) fold_try! |list, initial, step!| { var $state initial for item in list { $state step!($state, item)? } Ok($state) }类型签名逐项解读组成部分含义List(item)被折叠的列表元素类型为item多态state初始状态也是每次步骤函数的输入与输出类型(state, item - Try(state, err))步骤函数接收当前state与当前item返回Try(state, err)- Try(state, err)整体返回要么Ok(最终 state)要么Err(err)Try(state, err)即Result(state, err)的别名——要么是携带新状态的Ok要么是携带错误值的Err。核心机制?的短路展开实现的关键是循环体内的$state step($state, item)?其中?是 Roc 的错误传播运算符。fold_try的实现等价于for item in list { $state match step($state, item) { Ok(new_state) - new_state Err(err) - return Err(err) // 立即终止跳过后续元素 } } Ok($state)这正是第二条快照输出Err(Stop)的底层原因一旦某一步返回Err函数立即以该Err结束4不再参与折叠。副作用变体fold_try!fold_try!与fold_try结构完全相同区别在于步骤函数类型是(state, item Try(state, err))使用表示效果函数函数自身签名也用标注为效果函数因此只能在有对应能力capability的上下文中调用循环内调用改为step!($state, item)?。典型场景是把列表元素依次应用到某个有副作用如数据库写入、事件存储的更新函数上文档注释给出了示例events.fold_try!(State.init, |state, event| Store.apply!(state, event))它对每个元素运行步骤函数直到某个返回Err则停止见 Builtin.roc 的注释。四、边界条件与类型细节空列表fold_try对空列表直接返回Ok(initial)快照第 4 条验证了Ok(0.0)数字类型REPL 中未标注的数字默认按小数fractional推断6.0显式标注如1.I64后才得到整数结果6错误类型自由err是类型参数Err(Stop)中Stop是普通标签也可以是任何类型短路语义只要首个Err出现即终止不做收集所有错误式的聚合。五、动手复现在 REPL 中验证全部行为按 README.md 与 BUILDING_FROM_SOURCE.md 构建好roc可执行文件后运行交互式 REPLroc repl逐行粘贴快照 SOURCE 段的四条语句应得到与 OUTPUT 段完全一致的结果输入期望输出验证点[1, 2, 3].fold_try(0, \|sum, n\| Ok(sum n))Ok(6.0)全成功返回Ok(最终状态)[1, 2, 3, 4].fold_try(0, \|sum, n\| if n 3 { Ok(sum n) } else { Err(Stop) })Err(Stop)首个Err短路4被跳过[1.I64, 2, 3].fold_try!(0, \|sum, n\| Ok(sum n))Ok(6)显式类型 效果变体List.fold_try([], 0, \|sum, n\| Ok(sum n))Ok(0.0)函数式调用 空列表需要批量/回归验证时可使用快照工具roc snapshot --trace-eval test/snapshots/repl/list_fold_try.md如果输出与快照不一致测试即失败——这也是 src/snapshot_tool/main.zig 中 REPL 快照分支.repl节点类型所承担的角色把语言行为固化为可持续回归的契约。六、何时使用fold_try与fold/fold_until的取舍函数步骤函数返回终止条件适用场景List.fold普通state遍历完整个列表纯累加、聚合List.fold_untilContinue(state) / Break(state)步骤函数主动返回Break自定义提前终止无错误语义List.fold_tryTry(state, err)步骤函数返回Err或遍历完可失败折叠遇错即停List.fold_try!Try(state, err)效果函数同上带副作用/能力调用的可失败折叠典型场景累计校验一批数据、逐个执行可能失败的操作如批量写入并在首次失败时整体返回错误、或者用fold_try实现短路求值式的遍历。与之配套的还有keep_if_try见 Builtin.roc其实现同样依赖predicate(item)?的短路机制——理解fold_try也就理解了 Roc 中Try与?在遍历场景下的通用组合模式。总结List.fold_try(list, initial, step)返回Try(state, err)全成功得Ok(最终状态)首个Err立即短路并原样返回该错误实现位于 src/build/roc/Builtin.roc核心是循环体内的step($state, item)?错误传播fold_try!是效果函数变体步骤函数可执行带能力capability的副作用调用行为契约由 REPL 快照 test/snapshots/repl/list_fold_try.md 固化可通过roc repl或roc snapshot复现与回归。【免费下载链接】rocA fast, friendly, functional language.项目地址: https://gitcode.com/GitHub_Trending/ro/roc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考