ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Roc 语言 List.keep_oks 深度解析:从 REPL 快照测试看 Ok 值筛选与编译器验证

Roc 语言 List.keep_oks 深度解析:从 REPL 快照测试看 Ok 值筛选与编译器验证 Roc 语言 List.keep_oks 深度解析从 REPL 快照测试看 Ok 值筛选与编译器验证【免费下载链接】rocA fast, friendly, functional language.项目地址: https://gitcode.com/GitHub_Trending/ro/roc本文以 Roc 编译器仓库中的 REPL 快照测试 test/snapshots/repl/list_keep_oks.md 为核心骨架系统讲解List.keep_oks的函数签名、调用语义与输出行为并结合 src/build/roc/Builtin.roc 中的底层实现与test/下的回归测试揭示快照测试如何验证 REPL 求值结果与编译器行为。读完本文你将掌握List.keep_oks的完整用法含方法调用语法与函数式调用语法、类型标注与错误分支处理并理解 Roc 仓库中 REPL 快照文件META/SOURCE/OUTPUT/PROBLEMS的读写规范与运行命令能够自行运行、检查与更新这类快照测试。List.keep_oks 是什么筛选列表中的 Ok 值List.keep_oks是 Roc 标准库List模块提供的高阶函数它对列表中的每一个元素运行给定的转换函数transform保留所有被包装为Ok的结果值丢弃被包装为Err的结果最终返回一个新列表。在 src/build/roc/Builtin.roc 中可以看到它的完整签名与实现## Run the given function on each item of a list, and return a list of ## the values it wrapped in Ok. Items the function maps to Err are ## dropped. Use [List.keep_errs] to keep the Err values instead. ## roc ## expect [1.I64, 2, 3, 4].keep_oks(|n| if n.is_even() { Ok(n) } else { Err({}) }) [2, 4] ## keep_oks : List(before), (before - Try(after, _err)) - List(after) keep_oks |list, transform| List.fold( list, [], |acc, item| match transform(item) { Ok(after) List.concat(acc, [after]) Err(_) acc }, )签名解读keep_oks : List(before), (before - Try(after, _err)) - List(after)第一个参数list : List(before)输入列表元素类型为before第二个参数transform : before - Try(after, _err)一个对单个元素求值的函数返回Try(after, _err)即Ok(after)或Err(_err)的联合体返回值List(after)由所有Ok(after)中解包出的after值组成的新列表_err以下划线开头表示错误类型可以自由变化只要保证Err分支类型自洽即可这正是快照示例中可以写Err({})、Err(bad)等不同错误载荷的原因。实现要点基于 List.fold 的累积从源码可见keep_oks并不是一个特殊的编译器内建而是用List.fold从头到尾遍历累加器初始为空列表[]对每个元素执行transform(item)后做matchOk(after)分支List.concat(acc, [after])把解包值追加到结果尾部Err(_)分支原样返回acc即丢弃该元素。因此输出列表的顺序与输入列表一致且所有Err元素被过滤掉。这也意味着它是稳定的Ok值之间的相对顺序不会改变。REPL 实战演示四条命令与对应输出快照测试 test/snapshots/repl/list_keep_oks.md 的SOURCE段用»提示符记录了四组 REPL 交互OUTPUT段用---分隔符记录每次求值的精确输出。下面逐条拆解。1. 方法调用语法保留偶数» [1, 2, 3, 4].keep_oks(|n| if n.is_even() { Ok(n) } else { Err({}) })输出[2, 4]对[1, 2, 3, 4]逐元素判断2与4是偶数返回Ok(n)被保留1与3是奇数返回Err({})被丢弃注意Err({})使用空记录{}作为错误载荷——错误类型在这里是开放自由的这正是_err类型变量的典型用法输出[2, 4]是 REPL 通过Str.inspect序列化的字符串形式见下文REPL 的求值与序列化。2. 全部失败返回空列表» [1.I64, 2, 3].keep_oks(|_| Err({}))输出[]转换函数|_| Err({})对任何输入都返回Err因此没有任何Ok值可保留结果为空列表[]注意这里显式标注了首元素1.I641本身是数字字面量类型尚未确定标注I64后整个列表元素类型被统一为I64同时keep_oks的before类型也随之确定。3. 函数式调用语法 字符串匹配» mixed [a long heap-allocated string number one that will not fit inline, a long heap-allocated string number two that will not fit inline] » List.keep_oks(mixed, |s| if s.starts_with(a long heap-allocated string number one) { Ok(s) } else { Err({}) }) » mixed输出assigned mixed --- [a long heap-allocated string number one that will not fit inline] --- [a long heap-allocated string number one that will not fit inline, a long heap-allocated string number two that will not fit inline]三段输出对应三条交互mixed [...]是一条定义语句而非表达式因此 REPL 打印assigned \mixed并把定义纳入后续会话上下文List.keep_oks(mixed, ...)是函数式调用List.keep_oks前缀形式等价于mixed.keep_oks(...)的方法调用形式只保留以a long heap-allocated string number one开头的字符串故输出单元素列表再次输入mixed求值该变量本身输出两个堆分配长字符串组成的完整列表证明步骤 1 的定义确实生效且原列表未被修改——这也间接验证了keep_oks不会修改输入列表纯函数语义。输出对照表REPL 输入输出说明[1, 2, 3, 4].keep_oks(\|n\| if n.is_even() { Ok(n) } else { Err({}) })[2, 4]偶数的Ok被保留奇数的Err被丢弃[1.I64, 2, 3].keep_oks(\|_\| Err({}))[]全部返回Err结果为空列表mixed [...]assigned \mixed定义变量不产生求值结果List.keep_oks(mixed, \|s\| ...)[a long heap-allocated string number one ...]前缀式调用字符串前缀匹配mixed完整双元素列表原列表未被修改与 keep_errs / keep_if / drop_if 的对比keep_oks在标准库中并非孤立存在理解它的姊妹函数有助于掌握 Roc 的筛选函数家族。List.keep_errs保留 Err 值同一段源码src/build/roc/Builtin.roc紧随其后定义了keep_errskeep_errs : List(before), (before - Try(_ok, after)) - List(after) keep_errs |list, transform| List.fold( list, [], |acc, item| match transform(item) { Err(after) List.concat(acc, [after]) Ok(_) acc }, )语义正好相反保留Err载荷、丢弃Ok。对应的快照 test/snapshots/repl/list_keep_errs.md 演示了完整行为» [1, 2, 3, 4].keep_errs(|n| if n.is_even() { Ok(n) } else { Err(n) })输出[1, 3]——奇数作为Err(n)载荷被保留偶数作为Ok被丢弃。若|n| Ok(n)恒返回Ok则结果为[]。List.keep_if / drop_if基于 Bool 谓词keep_oks的输入是返回Try的转换函数而keep_if接收的是返回Bool的谓词src/build/roc/Builtin.rockeep_if : List(a), (a - Bool) - List(a) keep_if |list, predicate| List.fold( list, [], |acc, item| if predicate(item) { List.concat(acc, [item]) } else { acc }, )例如 test/snapshots/repl/list_keep_if.md» List.keep_if([1, 2, 3, 4, 5], |x| x 2)输出[3.0, 4.0, 5.0]REPL 中未绑定数字默认按F64显示。drop_if则保留谓词返回False的元素。选择建议当筛选逻辑天然以成功/失败建模如解析、查找、校验时用keep_oks/keep_errs错误分支携带诊断信息当只需按条件保留元素时用keep_if/drop_if。源码级深入调用链与类型细节REPL 的求值与序列化机制为什么快照中输出的是[2, 4]而非List [2, 4]这源于 Roc REPL 对表达式求值后统一调用Str.inspect进行序列化。在 src/cli/ReplSession.zig 的evaluateExpression中可以看到每条 REPL 表达式会被包裹成main || Str.inspect((expr))然后通过检查模块的 inspected-evaluation APIeval.Inspected.publishProgramKeepingReportedComptimeProblemsWithBuiltinAndContext在编译期求值把Str结果直接归档进ConstStore。这意味着快照OUTPUT段的每一行本质上都是该表达式Str.inspect后的精确字符串——这正是快照可以作为黄金输出逐字符比对的根基。数字类型的推断快照第 2 条命令显式写了1.I64这与 Roc 数字字面量的类型推断规则有关未标注的数字字面量是未绑定数字REPL 会结合上下文推断一旦列表中出现显式I64元素整个列表及keep_oks的类型参数就被固定为I64。快照中[1, 2, 3, 4]未标注任何元素输出却显示为[2, 4]整数形式说明 REPL 按整数字面量默认推断而list_keep_if.md中[1, 2, 3, 4, 5]输出为[3.0, 4.0, 5.0]则是该场景下数字被推断为F64所致。两个快照恰好覆盖了这两种推断路径。底层求值测试Try 回调的活性分支keep_oks的多态签名在求值层还有一个关键点当回调恒定返回同一分支时另一分支的类型可能从 monotype 中消失。仓库用两组 low-level 求值测试src/eval/test/eval_low_level_tests.zig验证了这一行为low_level - polymorphic Try callback keeps inactive Err branchalways_ok_n |_| Ok(1)配合自定义keep_oks期望[1.0]low_level - polymorphic Try callback keeps active Err payloadalways_err |_| Err(bad)期望[]。两个用例都使用与Builtin.roc同构的List.fold实现确认在常量回调下求值器仍能正确处理不因某个 tag 缺失而崩溃也不丢失载荷。回归测试monomorphize 阶段 panictest/fx/keep_oks.roc 记录了一个历史回归当自定义keep_oks以恒返回Ok的回调调用时monomorphize 阶段可能因 tag Err missing from monotype 而 panic——因为 monotype 中只有Oktag而match同时期望Ok与Err。该测试通过main!实际运行keep_oks([10], always_ok_n) [1]并打印done来守护该场景与快照测试形成互补快照验证 REPL 语义输出fx 测试验证编译/求值链路不崩溃。REPL 快照文件格式详解快照文件不是普通的 README而是可执行的编译器行为契约。以 test/snapshots/repl/list_keep_oks.md 为例其结构为四个段落META元信息descriptionList.keep_oks keeps the Ok values typerepldescription一句话描述该快照验证的行为便于索引与检索typerepl声明这是 REPL 快照快照工具会走交互式求值路径对应 src/snapshot_tool/main.zig 中的.repl分支。SOURCE输入» [1, 2, 3, 4].keep_oks(|n| if n.is_even() { Ok(n) } else { Err({}) }) » [1.I64, 2, 3].keep_oks(|_| Err({})) » mixed [...] » List.keep_oks(mixed, |s| ...) » mixed每行以»开头表示一条 REPL 输入mixed [...]是定义语句其余为求值表达式。这些行会按顺序送入 REPL 会话执行。OUTPUT期望输出[2, 4] --- [] --- assigned mixed --- [a long heap-allocated string number one that will not fit inline] --- [a long heap-allocated string number one that will not fit inline, a long heap-allocated string number two that will not fit inline]每条输入对应一段输出段与段之间用---分隔定义语句输出assigned \表达式输出Str.inspect后的字符串如[2, 4]快照工具会将实际输出与这里逐字符比对不一致即报告失败。PROBLEMS诊断报告NILNIL表示编译该快照的SOURCE时没有产生任何诊断报告。据 test/snapshots/README.md 说明若存在报告此段会以 canonical S-expression 序列化每个reporting.Report严重级别、标题、源码区域、标注等且不含终端渲染细节NIL意味着List.keep_oks相关调用在类型检查阶段完全通过——这本身就是对 API 合法用法的一次负向验证确保正确代码不产生误报诊断。如何运行、检查与更新快照仓库的 test/snapshots/README.md 给出了快照工具的完整用法核心命令如下# 生成/校验全部快照 zig build run-snapshot-tool # 只处理指定快照文件 zig build run-snapshot-tool -- test/snapshots/repl/list_keep_oks.md # 校验 EXPECTED 段与当前实际输出是否一致CI 常用 zig build run-snapshot-tool -- test/snapshots/repl/list_keep_oks.md --check-expected # 用实际输出覆盖更新 EXPECTED/DEV OUTPUT 段 zig build run-snapshot-tool -- test/snapshots/repl/list_keep_oks.md --update-expected # 调试 REPL 求值过程仅限单个 REPL 快照 zig build run-snapshot-tool -- test/snapshots/repl/list_keep_oks.md --trace-eval--trace-eval会开启解释器逐步跟踪是调试 REPL 快照求值结果的利器在 release 构建下需额外传-Dtrace-evaltrue才能启用跟踪输出。快照工具还支持其他参数见 src/snapshot_tool/main.zig 的--help输出参数作用--verbose开启详细日志--html同时生成 HTML 输出文件--debug关闭线程局部 arena暴露内存分配缺陷--linecol输出中包含行列号信息--threads n并发线程数0 为自动、上限 41 为单线程--check-expected/--update-expected校验 / 更新 EXPECTED 段--check-output/--update-output校验 / 更新 DEV OUTPUT 段--fuzz-corpus path指定 fuzz 语料路径结论List.keep_oks是 Roc 标准库中按 Try 结果筛选列表的核心工具通过List.foldList.concat的稳定累积实现保留所有Ok载荷、丢弃Err支持方法调用与函数式调用两种语法并与keep_errs、keep_if、drop_if构成完整的筛选函数家族。而 REPL 快照测试则以META/SOURCE/OUTPUT/PROBLEMS四段结构把 REPL 求值结果与编译器诊断固化为可逐字符比对的黄金输出配合 src/snapshot_tool/main.zig 的命令行工具、src/eval/test/eval_low_level_tests.zig 的求值测试与 test/fx/keep_oks.roc 的回归测试从语义、求值、类型推断、诊断零报告四个维度守护该 API 的行为契约。对 Roc 学习者而言读懂这份快照即读懂了keep_oks的完整行为对编译器贡献者而言它是一份可直接复现的验证样例。【免费下载链接】rocA fast, friendly, functional language.项目地址: https://gitcode.com/GitHub_Trending/ro/roc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表