Files
endorphin-tabularium-docs/content/docs/language-design/11-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

8.8 KiB
Raw Blame History

11 · 模块与程序执行

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

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

11-01 模块的职责与顶层内容

  • 待讨论

明确模块主要承载声明、可执行初始化,还是可实例化的定义;说明导入与执行的关系。

11-02 模块的组织单位

  • 待讨论

明确模块与文件、目录、包的对应关系,以及嵌套模块、多文件模块是否支持;说明不合法的组织形式。

11-03 模块身份

  • 待讨论

明确判断“同一个模块”的依据;覆盖路径别名、重复引用及不同版本,说明身份冲突如何处理。

11-04 名称与路径解析

  • 待讨论

明确绝对引用、相对引用、搜索位置及优先级;说明找不到目标、存在多个候选时的行为。

11-05 依赖与版本选择

  • 待讨论

明确依赖由谁声明、版本由谁选择,能否同时使用多个版本;划清语言、构建工具和包管理器的职责。

11-06 导出与可见性

  • 待讨论

明确默认公开范围、显式导出及内部成员的访问规则;说明越权引用如何失败。依赖【名称与作用域】。

11-07 导入形式与名称冲突

  • 待讨论

明确整体导入、成员导入、别名、重新导出和批量导入的支持范围;说明重名与遮蔽规则。

11-08 跨模块绑定语义

  • 待讨论

明确导入获得值、持续关联的绑定,还是其他形式;说明重新赋值和修改共享对象的区别。依赖【名称与作用域】【对象与类型模型】。

11-09 依赖检查与条件导入

  • 待讨论

明确依赖必须何时可用,是否支持可选或条件导入;说明未使用但缺失的依赖是否导致失败。依赖【编译与执行方式】。

11-10 初始化触发与次数

  • 待讨论

明确初始化何时发生、何种范围内执行几次,以及重复导入的行为。

11-11 初始化顺序与并发

  • 待讨论

明确依赖之间的初始化顺序,是否允许并发或异步初始化;说明顺序不确定时有哪些保证。依赖【并发与异步】。

11-12 循环依赖

  • 待讨论

明确允许哪些循环,何时检测;说明访问尚未准备好的定义或状态如何处理。

11-13 初始化失败

  • 待讨论

明确失败后模块处于什么状态,能否重试,如何处理已发生的副作用,以及依赖方会得到什么结果。依赖【错误处理】。

11-14 模块状态与隔离

  • 待讨论

明确是否允许可变模块状态、由谁持有、何时结束,以及多个程序环境能否相互隔离。依赖【内存与资源管理】。

11-15 程序入口的确定

  • 待讨论

明确入口是指定函数、指定模块、顶层代码还是其他形式;说明缺失或存在多个入口时如何处理。

11-16 入口的输入输出契约

  • 待讨论

明确参数如何传入、入口允许返回什么,以及如何形成退出结果;说明不合法的入口签名。依赖【类型系统】【标准库与宿主接口】。

11-17 启动流程

  • 待讨论

明确依赖准备、模块初始化与入口执行的先后关系;说明启动中途失败时入口和清理是否执行。

11-18 动态装载与模块生命周期

  • 待讨论

明确是否支持运行中加载、重新加载和卸载;如支持,说明旧引用、状态与资源的去向;如不支持,记录限制。

11-19 正常结束

  • 待讨论

明确入口返回、显式退出与后台工作之间的关系;说明哪些条件代表程序真正结束。依赖【并发与异步】。

11-20 异常与外部终止

  • 待讨论

明确未处理错误、取消、外部终止请求和强制终止分别有什么保证;说明错误报告与退出结果。依赖【错误处理】【并发与异步】【宿主接口】。

11-21 清理协议与顺序

  • 待讨论

明确清理动作如何注册或归属、执行顺序、能否异步,以及清理自身失败或重复触发的处理。依赖【内存与资源管理】【错误处理】。

11-22 跨分类一致性验收

  • 待讨论

用普通启动、共享依赖、循环依赖、初始化失败和退出清理等完整场景检查规则;相关分类决定已对齐,且没有未解决的阻塞,才可完成。

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

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

当前仍是 11-01:模块的职责与顶层内容。要决定的是:模块顶层能否执行读取配置、打开连接等动作,以及这些动作是否由导入自动触发。

相关语言的对照如下。下述好处与代价是设计取舍分析,不是在断言它们的历史设计动机。

语言 规则与具体行为 取舍及对我们的启发
Python 对普通源码模块,常规首次导入会执行模块代码。例如顶层 current = read_config() 会在导入时调用函数;后续命中模块缓存时通常不再执行。这里说的是 Python 导入规则,并非 CPython 独有的优化。官方参考 顶层写法直接,容易提供共享状态;但一次导入可能读文件、修改环境或抛出异常。对应我们的 B。
JS:ES Modules 模块允许顶层语句。例如模块中写 console.log("loaded"),该模块被求值时就会打印;普通静态导入会使依赖参与求值。导入声明的连接过程与模块代码求值是不同阶段。这里专指 ESM,CommonJS 另有规则。ECMAScript 规范 能自动执行依赖的准备动作;也需要处理副作用、初始化顺序和失败。提醒我们区分“找到并连接定义”和“执行初始化”。
TS 运行时模块行为由实际采用的 JS 模块格式和宿主决定,TS 不提供独立运行时。import type { Config } ... 会从输出 JS 中消除,因此这条导入本身不会触发运行时加载。官方文档 类型依赖可以与运行时依赖分开,但要明确两者的区别。启发是:需要一个类型,不一定需要运行其所属模块;是否采用该机制留给后续任务。
Go 包顶层不能任意写执行语句,但变量初始化可以调用函数,如 var current = readConfig();还可以写自动执行的 func init()。程序启动时先初始化依赖包,再运行入口。语言规范 顶层结构规整,仍可自动准备共享状态;副作用仍可能藏在变量初始化里。说明**“顶层只准声明”并不等于“没有自动执行”**。
Rust 模块是定义项的容器,没有普通的“导入即执行模块体”机制。直接在模块体中写 read_config(); 不合法;普通 static 的初始化表达式要求能在编译期求值,不能直接读取运行时配置文件。可把读取动作放入函数,由调用者调用。模块规则、静态项规则 普通模块使用不会自动读取配置,启动动作更明确;调用者需要组织初始化。对应我们的 A 方向。

还有一点需要澄清:A、B 两种模块都可以导出 create(config) 这样的普通函数,用它创建多个独立状态。 只有当我们希望语言本身支持“创建模块实例、给实例连接依赖”时,才需要之前的 C;普通对象或函数已经能解决不少隔离需求。

因此当前三个选择保持不变:

  • A.显式调用:模块提供定义;读取配置等运行时动作放入函数,由调用者启动。还需明确允许哪些顶层初始化表达式,才能保证声明中不会暗藏这些动作。
  • B.自动初始化:模块可以带运行时初始化,使用模块时由系统安排执行;触发时机、次数和失败处理分别在后续任务决定。
  • C.显式模块实例:导入定义后,由调用者创建带状态的模块实例;语言需要提供实例创建和依赖连接规则。

我暂建议 A:它让初始化失败发生在明确的调用处,也方便调用者决定状态如何共享。代价是程序需要多写一些启动与状态传递代码。如果你希望模块直接提供“准备好的共享服务”,B 会更顺手。这个建议尚未成为决定。

讨论来源

《语言设计 11:模块与程序执行》,整理日期 2026-10-28。章节编号沿用原清单。