197 lines
7.6 KiB
Markdown
197 lines
7.6 KiB
Markdown
# 02 · 名称与作用域
|
|
|
|
13/22 项已确认。当前推进 02-15;02-11 的特殊结构作用域仍需核对。
|
|
|
|
本页整理原讨论的问题、验收要求和候选方案。勾选代表讨论中已确认的规则,不代表编译器或运行时已经实现。
|
|
|
|
## 02-01 词法作用域
|
|
|
|
- [x] 已确认
|
|
|
|
名称按定义位置的代码嵌套关系查找;调用者的局部变量不会参与被调用函数的名称查找。
|
|
|
|
## 02-02 普通变量的基本作用范围
|
|
|
|
- [x] 已确认
|
|
|
|
不需要声明关键词;函数内普通变量采用 Python 式作用范围,普通 `if`、`for` 块不限制它。块内赋值后,块外可以使用,前提是该赋值确实发生。
|
|
|
|
## 02-03 `let` 临时绑定
|
|
|
|
- [x] 已确认
|
|
|
|
`let` 声明的变量限定在所属块内;后续赋值仍操作该临时绑定,不扩大作用域;不提供 `var`。
|
|
|
|
## 02-04 内层遮蔽外层
|
|
|
|
- [x] 已确认
|
|
|
|
内层 `let` 可以与外层变量同名。块内读写内层绑定,退出后恢复访问外层绑定,外层值不受这些修改影响。
|
|
|
|
## 02-05 同层重复 `let`
|
|
|
|
- [x] 已确认
|
|
|
|
同一作用域重复声明同名 `let` 报错;修改已有变量使用普通赋值。
|
|
|
|
## 02-06 `let` 声明前的直接使用
|
|
|
|
- [x] 已确认
|
|
|
|
整个块中的同名直接读写都认定为该临时绑定;声明前使用在编译检查阶段报错,不会转而访问外层同名变量。
|
|
|
|
## 02-07 分支与未初始化读取
|
|
|
|
- [x] 已确认
|
|
|
|
确定不能保证读取前已赋值时,编译报错还是运行时报错;覆盖条件分支、可能执行零次的循环和提前退出。
|
|
|
|
**已确认规则:**采用编译时的确定赋值检查。每条能够到达读取位置的路径都必须先赋值;编译器无法证明这一点,就报错。
|
|
|
|
## 02-08 普通变量的声明前查找
|
|
|
|
- [x] 已确认
|
|
|
|
确定函数后面出现普通赋值时,前面的同名读取算本地未初始化,还是可以访问外层绑定。
|
|
|
|
**已确认规则:**局部归属按整个函数确定,赋值前读取不能回退到外层同名变量。
|
|
|
|
## 02-09 同层名称冲突
|
|
|
|
- [x] 已确认
|
|
|
|
明确参数、普通变量、`let` 之间哪些组合属于赋值、哪些属于重复声明或冲突。
|
|
|
|
**已确认规则:**参数后的普通赋值修改参数绑定;参数与函数体同层 let 同名、同层重复 let、重复参数名均编译报错;更内层 let 可以遮蔽;let 之前的同名赋值属于声明前访问,编译报错。
|
|
|
|
## 02-10 内层函数修改外层绑定
|
|
|
|
- [x] 已确认
|
|
|
|
明确普通赋值默认修改哪一层,是否需要专门语法才能修改外层变量。
|
|
|
|
**已确认规则:**普通赋值默认建立或修改当前函数的局部绑定;通过 `nonlocal` 显式指定外层函数中已有的绑定。
|
|
|
|
## 02-11 各语法结构的作用域边界
|
|
|
|
- [ ] 待讨论
|
|
|
|
列清函数、独立块、循环及其他受支持结构各自如何引入绑定;未决定是否支持的结构标注依赖。依赖:**语法设计**。
|
|
|
|
## 02-12 循环绑定
|
|
|
|
- [x] 已确认
|
|
|
|
确定循环变量和循环体中的 `let` 是每轮建立新绑定,还是多轮复用;说明闭包捕获后的结果。
|
|
|
|
**已确认规则:**每次进入循环体,创建这一轮自己的 `let` 绑定;普通变量继续复用所属函数中的绑定。循环外不能访问循环体内的 `let`。若支持循环头的 `let`,也采用每轮独立绑定的原则。
|
|
|
|
## 02-13 声明与初始化顺序
|
|
|
|
- [x] 已确认
|
|
|
|
明确 `let` 能否不带初值、初始化表达式中的同名引用如何处理,以及受支持的复合声明按什么顺序建立绑定。依赖:**语法设计**。
|
|
|
|
**已确认规则:**允许先声明后赋值;初始化前读取自身报错;多目标声明或赋值先求完整右侧,再绑定目标。具体复合语法由语法设计对齐。
|
|
|
|
## 02-14 闭包捕获什么
|
|
|
|
- [x] 已确认
|
|
|
|
确定默认捕获绑定还是当时的值,以及是否提供显式选择。
|
|
|
|
**已确认规则:**默认捕获绑定;外层重新赋值后,闭包读到新值。需要快照时手动建立独立绑定,不自动深拷贝对象。
|
|
|
|
## 02-15 闭包修改与共享
|
|
|
|
- [ ] 待讨论
|
|
|
|
确定多个闭包是否共享同一外层绑定、哪些修改允许、哪些情况报错。依赖:**可变性规则**。
|
|
|
|
## 02-16 逃逸闭包的绑定存续
|
|
|
|
- [ ] 待讨论
|
|
|
|
确定外层函数返回或代码块退出后,闭包仍可访问什么,以及如何保证访问有效。依赖:**运行时实现、资源管理**。
|
|
|
|
## 02-17 完整名称查找顺序
|
|
|
|
- [ ] 待讨论
|
|
|
|
明确函数外层、模块、内置名称的查找顺序,以及类成员是否参与裸名称查找。依赖:**模块系统、对象与类型模型、内置库设计**。
|
|
|
|
## 02-18 模块可见性
|
|
|
|
- [ ] 待讨论
|
|
|
|
明确默认私有还是公开、如何导出,以及访问不可见名称时的错误。依赖:**模块系统**。
|
|
|
|
## 02-19 导入名称的绑定与冲突
|
|
|
|
- [ ] 待讨论
|
|
|
|
明确导入名能否重新赋值、别名和重名如何处理、导入后观察到的是哪个绑定。依赖:**模块系统**。
|
|
|
|
## 02-20 跨模块初始化
|
|
|
|
- [ ] 待讨论
|
|
|
|
明确循环导入或模块尚未初始化时,读取其名称如何处理。依赖:**模块系统、执行模型**。
|
|
|
|
## 02-21 类型名与值名的关系
|
|
|
|
- [ ] 待讨论
|
|
|
|
确定是否共享命名空间、同名是否冲突、名称如何区分解析。依赖:**类型系统**。
|
|
|
|
## 02-22 最终一致性验收
|
|
|
|
- [ ] 待讨论
|
|
|
|
用一组正常与错误示例交叉检查上述规则,解决跨分类冲突,形成最终规则摘要。
|
|
|
|
## 当前问题的方案与语言对照
|
|
|
|
以下沿用原讨论材料;建议和参考语言的做法均不自动成为本语言规则。
|
|
|
|
接下来是 **02-15:多个闭包能否共同读写同一个绑定**。
|
|
|
|
```text
|
|
fn example() {
|
|
count = 0
|
|
|
|
fn increase() {
|
|
nonlocal count
|
|
count = count + 1
|
|
}
|
|
|
|
fn read() {
|
|
return count
|
|
}
|
|
|
|
increase()
|
|
print(read()) // 如果允许共享,输出 1
|
|
}
|
|
```
|
|
|
|
各语言的相关规则:
|
|
|
|
| 语言 | 做法 | 收益与代价 |
|
|
|---|---|---|
|
|
| **Python** | 多个闭包可以共享绑定;修改外层函数绑定需要 `nonlocal` | 共享状态方便,修改也有明确标记;多个函数的行为会相互影响。[官方说明](https://docs.python.org/3/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value) |
|
|
| **JS / TS** | 可以共享并修改同一绑定,不需要 `nonlocal`;TS 保持 JS 的运行语义 | 写法直接,读代码时需要留意哪些闭包会修改变量。[官方手册](https://www.typescriptlang.org/docs/handbook/variable-declarations.html) |
|
|
| **Go** | 外层函数与闭包共享变量,允许修改 | 共享直接;并发执行时需要另行协调访问。[规范](https://go.dev/ref/spec#Function_literals) |
|
|
| **Rust** | 借用捕获受访问限制约束;相互冲突的可变借用和只读借用不能同时存续。共享可变状态需要合适的显式机制 | 编译器能限制冲突访问,但需要更多所有权与借用规则。[官方说明](https://doc.rust-lang.org/book/ch13-01-closures.html#capturing-references-or-moving-ownership) |
|
|
|
|
我们有两个主要方向:
|
|
|
|
1. **允许多个闭包共享读写**:上面例子合法,输出 `1`;修改仍要求 `nonlocal`。
|
|
2. **限制共享可变捕获**:某些同时持有读写访问的闭包组合被拒绝;具体限制需要与所有权模型一起设计。
|
|
|
|
我推荐 **1**,与前四种语言的方向一致,也能延续已经采用的 `nonlocal`。这里先决定单线程中的共享行为;并发访问规则属于并发模型。
|
|
|
|
## 讨论来源
|
|
|
|
《语言设计 02:名称与作用域》,整理日期 2026-10-28。章节编号沿用原清单。
|
|
|