Files
endorphin-tabularium-docs/content/docs/language-design/10-questions.md
T
codex-bot ef1ba88161
Verify and deploy Tabularium / verify-and-deploy (push) Waiting to run
Add language design question collection with discussion decisions and provenance
2026-10-28 14:02:12 +08:00

7.2 KiB
Raw Blame History

10 · 错误与资源

0/16 项已确认。当前推进 10-01;本分类尚未完成选择。

本页整理原讨论的问题、验收要求和候选方案。勾选代表讨论中已确认的规则,不代表编译器或运行时已经实现。

10-01 失败的基本控制流

  • 待讨论

确定使用错误结果、异常还是混合机制,以及失败后调用处如何继续。

10-02 错误的表示与分类

  • 待讨论

确定错误携带哪些信息、如何区分类别和匹配处理器,是否允许自定义错误。依赖【对象与类型模型】。

10-03 失败是否属于接口契约

  • 待讨论

确定函数是否声明可能的失败、调用者是否必须处理,以及遗漏处理如何检查。依赖【类型系统】。

10-04 向上传播与机制转换

  • 待讨论

确定自动或显式传播规则,以及错误结果与异常之间是否允许转换、转换保留什么信息。

10-05 捕获与恢复

  • 待讨论

确定处理器的匹配顺序、可见范围、重新传播,以及处理后从哪里继续;明确是否支持回到失败点继续执行。

10-06 退出作用域与栈展开

  • 待讨论

确定错误跨越调用和作用域时的退出顺序,哪些清理必须执行,以及清理能访问哪些仍有效的数据。

10-07 清理结构与执行时机

  • 待讨论

决定支持哪些 finally、defer 或资源作用域机制,明确注册时机、执行顺序,以及正常结束、提前返回和错误退出的行为。

10-08 资源责任与关闭规则

  • 待讨论

确定谁负责关闭、责任能否转移,以及共享、重复关闭和关闭后使用如何处理。依赖【所有权与生命周期/类型系统】。

10-09 清理过程中再次失败

  • 待讨论

确定原始错误与清理错误如何保留,后续清理是否继续,以及最终向外报告什么。

10-10 析构、终结与 GC 的边界

  • 待讨论

确定是否提供析构或终结机制、执行保证及限制,明确内存回收能否承担资源关闭责任。依赖【内存管理与运行时】。

10-11 不可恢复失败与终止

  • 待讨论

区分可处理失败、程序缺陷和强制终止;确定各类情况下是否展开和清理,以及未处理错误到达程序边界后的行为。

10-12 异步操作与取消中的清理

  • 待讨论

明确是否支持异步清理、取消如何传播,以及清理期间再次取消的行为。依赖【并发与异步】。

10-13 并发中的多个失败

  • 待讨论

确定任务失败由谁接收,多个失败如何组合,其他任务是否继续或取消。依赖【并发与异步】。

10-14 资源协议与外部接口边界

  • 待讨论

明确语言内置保证与库协议的分工,以及外部调用的错误如何进入本语言、跨越外部调用时谁负责清理。依赖【定制协议、标准库与外部互操作】。

10-15 错误诊断与上下文保留

  • 待讨论

确定是否保留调用位置、传播路径、原始原因与附加上下文,以及包装、重抛后保留什么。

10-16 跨分类一致性验收

  • 待讨论

用正常返回、嵌套失败、清理失败、未处理失败及适用的取消场景核对全部规则,消除与其他分类决定的冲突和阻塞。

当前问题的方案与语言对照

以下沿用原讨论材料;建议和参考语言的做法均不自动成为本语言规则。

当前仍是 10-01:常规操作失败时,返回失败结果,还是直接中断控制流? 这里先讨论文件不存在、配置格式错误等情况,致命故障留到 10-11。

相关语言怎样做

语言 规则与小例子 好处、代价与启发
Python 广泛使用异常。例如 int("abc") 抛出 ValueError,跳过当前路径中的后续语句,寻找匹配的 except;中间函数不必逐层转发。也允许接口返回普通错误结果。官方文档 正常路径紧凑,便于外层集中处理;调用表达式本身通常看不出可能抛什么。启发:选择异常,就要接受并管理这种隐式退出路径。
JS 支持 throw 和 try/catch,可抛出任意值。例如 throw "bad config" 会中断当前路径,由外层捕获。语言规范 传播方便,但处理器不能天然假定拿到的是标准错误对象。启发:采用异常与允许抛什么是两个独立决定。
TS 运行时沿用 JS 的异常行为。静态检查可以约束错误的使用:启用 useUnknownInCatchVariables 时,捕获值默认为 unknown,访问其属性前需要缩小类型;这不等于静态列出函数可能抛出的错误。官方文档 能提高处理代码的安全性,却不会自动消除隐式异常路径。启发:静态类型检查与失败控制流要分别设计。
Go 常规错误通常作为额外返回值,例如得到 (文件, error) 后检查 err != nil;返回错误本身不会自动退出调用者。另有 panic/recover 机制。官方说明 检查与返回位置明确;逐层传播容易重复,也可能忽略错误。启发:显式错误值和另一种中断机制可以并存,但需要明确用途。
Rust 常规可恢复错误使用 Result<T, E>,区分 Ok 与 Err。例如 File::open(path)? 在失败时提前返回当前函数;不用 ? 时,可检查或传递这个结果。官方文档 结果结构清楚,传播简写减少重复;需要相应的类型与转换规则。启发:显式结果并不要求每次手写完整分支,传播语法可以另行设计。

这些实践提供参考,不替我们作决定;表中的好处与代价是取舍分析,不是对历史设计动机的断言。

我们当前的三个候选方案

沿用“配置文件不存在就使用默认配置,格式错误就停止启动”的例子:

  • A.常规失败统一返回显式结果:调用者检查或显式传播。可以随后讨论传播简写,无须现在确定类型语法。
  • B.常规失败统一通过异常传播:调用者捕获“文件不存在”来恢复,格式错误继续向外传播。
  • C.两者并存:允许不同接口选择,但必须进一步确定分工,避免调用者不清楚该检查返回值还是捕获异常。

我的暂时建议是优先考虑 A:它让失败路径容易在调用处辨认,也能通过传播简写减少重复。如果你更重视多层调用后集中处理错误,B 更值得选。选择 C 则需要有明确的两类使用场景,收益才足以抵消复杂度。语言用途尚未确定,这个建议仍是候选。

你更倾向 A、B,还是 C?

讨论来源

《语言设计 10:错误与资源》,整理日期 2026-10-28。章节编号沿用原清单。