Files
endorphin-tabularium-docs/content/docs/language-design/02-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.6 KiB
Raw Blame History

02 · 名称与作用域

13/22 项已确认。当前推进 02-15;02-11 的特殊结构作用域仍需核对。

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

02-01 词法作用域

  • 已确认

名称按定义位置的代码嵌套关系查找;调用者的局部变量不会参与被调用函数的名称查找。

02-02 普通变量的基本作用范围

  • 已确认

不需要声明关键词;函数内普通变量采用 Python 式作用范围,普通 if、for 块不限制它。块内赋值后,块外可以使用,前提是该赋值确实发生。

02-03 let 临时绑定

  • 已确认

let 声明的变量限定在所属块内;后续赋值仍操作该临时绑定,不扩大作用域;不提供 var。

02-04 内层遮蔽外层

  • 已确认

内层 let 可以与外层变量同名。块内读写内层绑定,退出后恢复访问外层绑定,外层值不受这些修改影响。

02-05 同层重复 let

  • 已确认

同一作用域重复声明同名 let 报错;修改已有变量使用普通赋值。

02-06 let 声明前的直接使用

  • 已确认

整个块中的同名直接读写都认定为该临时绑定;声明前使用在编译检查阶段报错,不会转而访问外层同名变量。

02-07 分支与未初始化读取

  • 已确认

确定不能保证读取前已赋值时,编译报错还是运行时报错;覆盖条件分支、可能执行零次的循环和提前退出。

**已确认规则:**采用编译时的确定赋值检查。每条能够到达读取位置的路径都必须先赋值;编译器无法证明这一点,就报错。

02-08 普通变量的声明前查找

  • 已确认

确定函数后面出现普通赋值时,前面的同名读取算本地未初始化,还是可以访问外层绑定。

**已确认规则:**局部归属按整个函数确定,赋值前读取不能回退到外层同名变量。

02-09 同层名称冲突

  • 已确认

明确参数、普通变量、let 之间哪些组合属于赋值、哪些属于重复声明或冲突。

**已确认规则:**参数后的普通赋值修改参数绑定;参数与函数体同层 let 同名、同层重复 let、重复参数名均编译报错;更内层 let 可以遮蔽;let 之前的同名赋值属于声明前访问,编译报错。

02-10 内层函数修改外层绑定

  • 已确认

明确普通赋值默认修改哪一层,是否需要专门语法才能修改外层变量。

**已确认规则:**普通赋值默认建立或修改当前函数的局部绑定;通过 nonlocal 显式指定外层函数中已有的绑定。

02-11 各语法结构的作用域边界

  • 待讨论

列清函数、独立块、循环及其他受支持结构各自如何引入绑定;未决定是否支持的结构标注依赖。依赖:语法设计。

02-12 循环绑定

  • 已确认

确定循环变量和循环体中的 let 是每轮建立新绑定,还是多轮复用;说明闭包捕获后的结果。

**已确认规则:**每次进入循环体,创建这一轮自己的 let 绑定;普通变量继续复用所属函数中的绑定。循环外不能访问循环体内的 let。若支持循环头的 let,也采用每轮独立绑定的原则。

02-13 声明与初始化顺序

  • 已确认

明确 let 能否不带初值、初始化表达式中的同名引用如何处理,以及受支持的复合声明按什么顺序建立绑定。依赖:语法设计。

**已确认规则:**允许先声明后赋值;初始化前读取自身报错;多目标声明或赋值先求完整右侧,再绑定目标。具体复合语法由语法设计对齐。

02-14 闭包捕获什么

  • 已确认

确定默认捕获绑定还是当时的值,以及是否提供显式选择。

**已确认规则:**默认捕获绑定;外层重新赋值后,闭包读到新值。需要快照时手动建立独立绑定,不自动深拷贝对象。

02-15 闭包修改与共享

  • 待讨论

确定多个闭包是否共享同一外层绑定、哪些修改允许、哪些情况报错。依赖:可变性规则。

02-16 逃逸闭包的绑定存续

  • 待讨论

确定外层函数返回或代码块退出后,闭包仍可访问什么,以及如何保证访问有效。依赖:运行时实现、资源管理。

02-17 完整名称查找顺序

  • 待讨论

明确函数外层、模块、内置名称的查找顺序,以及类成员是否参与裸名称查找。依赖:模块系统、对象与类型模型、内置库设计。

02-18 模块可见性

  • 待讨论

明确默认私有还是公开、如何导出,以及访问不可见名称时的错误。依赖:模块系统。

02-19 导入名称的绑定与冲突

  • 待讨论

明确导入名能否重新赋值、别名和重名如何处理、导入后观察到的是哪个绑定。依赖:模块系统。

02-20 跨模块初始化

  • 待讨论

明确循环导入或模块尚未初始化时,读取其名称如何处理。依赖:模块系统、执行模型。

02-21 类型名与值名的关系

  • 待讨论

确定是否共享命名空间、同名是否冲突、名称如何区分解析。依赖:类型系统。

02-22 最终一致性验收

  • 待讨论

用一组正常与错误示例交叉检查上述规则,解决跨分类冲突,形成最终规则摘要。

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

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

接下来是 02-15:多个闭包能否共同读写同一个绑定。

fn example() {
    count = 0

    fn increase() {
        nonlocal count
        count = count + 1
    }

    fn read() {
        return count
    }

    increase()
    print(read())  // 如果允许共享,输出 1
}

各语言的相关规则:

语言 做法 收益与代价
Python 多个闭包可以共享绑定;修改外层函数绑定需要 nonlocal 共享状态方便,修改也有明确标记;多个函数的行为会相互影响。官方说明
JS / TS 可以共享并修改同一绑定,不需要 nonlocal;TS 保持 JS 的运行语义 写法直接,读代码时需要留意哪些闭包会修改变量。官方手册
Go 外层函数与闭包共享变量,允许修改 共享直接;并发执行时需要另行协调访问。规范
Rust 借用捕获受访问限制约束;相互冲突的可变借用和只读借用不能同时存续。共享可变状态需要合适的显式机制 编译器能限制冲突访问,但需要更多所有权与借用规则。官方说明

我们有两个主要方向:

  1. 允许多个闭包共享读写:上面例子合法,输出 1;修改仍要求 nonlocal。
  2. 限制共享可变捕获:某些同时持有读写访问的闭包组合被拒绝;具体限制需要与所有权模型一起设计。

我推荐 1,与前四种语言的方向一致,也能延续已经采用的 nonlocal。这里先决定单线程中的共享行为;并发访问规则属于并发模型。

讨论来源

《语言设计 02:名称与作用域》,整理日期 2026-10-28。章节编号沿用原清单。