# 07 · 函数与调用 0/22 项已确认。当前推进 07-01;本分类尚未完成选择。 本页整理原讨论的问题、验收要求和候选方案。勾选代表讨论中已确认的规则,不代表编译器或运行时已经实现。 ## 07-01 函数作为值【进行中】 - [ ] 待讨论 明确能否赋值、存入数据、作为参数和返回结果,以及受限情形。 ## 07-02 函数定义与创建 - [ ] 待讨论 明确具名函数、匿名函数是否支持;定义何时创建函数、何时可见,重复定义怎样处理。 ## 07-03 可调用目标 - [ ] 待讨论 明确哪些值可以调用,调用非函数如何失败;方法、可调用对象若存在,明确接入边界。依赖:**对象与类型模型**。 ## 07-04 调用的求值策略与顺序 - [ ] 待讨论 明确实参是立即求值还是延迟求值,调用目标与实参的求值顺序,以及求值中断后的行为。 ## 07-05 参数传递与修改 - [ ] 待讨论 明确形参赋值、修改实参所指数据分别有什么效果;是否支持引用、借用或其他传递方式。依赖:**变量与内存模型、类型系统**。 ## 07-06 位置参数 - [ ] 待讨论 明确按什么顺序绑定,是否支持仅限位置的参数;数量不足或过多怎样处理。 ## 07-07 命名参数 - [ ] 待讨论 明确是否支持、参数名是否属于公开接口、与位置参数如何混用,以及未知名称与重复绑定的处理。 ## 07-08 默认参数 - [ ] 待讨论 明确求值时机、可访问的名字、默认值之间的依赖、可变默认值的共享行为,以及省略与显式传空值的区别。 ## 07-09 可变参数与展开 - [ ] 待讨论 明确是否支持剩余位置参数、剩余命名参数及调用展开;规定收集形式、顺序、重复名称与非法展开。 ## 07-10 绑定失败的统一规则 - [ ] 待讨论 明确绑定失败何时发现、是否执行函数体、此前哪些表达式可能已执行,以及错误如何报告。依赖:**错误处理**。 ## 07-11 返回与无结果 - [ ] 待讨论 明确显式返回、执行到末尾、无结果与空值、多结果及接收数量不符的行为。 ## 07-12 递归与名字绑定 - [ ] 待讨论 明确自递归、相互递归、局部递归函数是否支持,以及重绑定函数名后递归调用指向什么。依赖:**作用域与模块**。 ## 07-13 尾调用与调用深度 - [ ] 待讨论 明确是否保证尾调用、哪些位置算尾位置,以及调用深度耗尽怎样失败。依赖:**控制流与运行时**。 ## 07-14 闭包的名字解析 - [ ] 待讨论 明确是否支持访问外层局部名字,采用词法作用域还是其他规则,以及遮蔽和未绑定名字的行为。依赖:**作用域**。 ## 07-15 闭包捕获与共享状态 - [ ] 待讨论 明确捕获值还是变量、自动还是显式捕获,以及多个闭包、循环变量与外部修改之间的关系。依赖:**变量与内存模型**。 ## 07-16 闭包逃逸与生命周期 - [ ] 待讨论 明确闭包能否比创建它的调用活得更久,捕获的资源如何保持有效,不能逃逸时如何拒绝。依赖:**资源管理与类型系统**。 ## 07-17 函数的调用接口 - [ ] 待讨论 明确函数签名包含哪些信息、怎样检查兼容性;重载或泛型若存在,明确调用选择的接口。依赖:**类型系统**。 ## 07-18 函数身份与比较 - [ ] 待讨论 明确函数是否可比较、复制后是否保持身份、重复创建的闭包有什么关系,以及能否作为键。依赖:**值与相等性模型**。 ## 07-19 非普通控制流的调用边界 - [ ] 待讨论 明确异常、异步、生成器等若被采用,对调用与返回产生什么影响;只规定函数侧接口。依赖:**错误处理、并发与控制流**。 ## 07-20 内置函数与外部函数 - [ ] 待讨论 明确它们是否遵守同样的参数、返回和错误规则,以及无法兼容时如何声明差异。依赖:**内置与标准库、外部互操作**。 ## 07-21 语言调用语义与底层约定 - [ ] 待讨论 明确哪些行为属于语言保证,哪些由实现决定;外部调用需要哪些额外约定,避免把栈、寄存器布局误当语言规则。 ## 07-22 跨分类一致性验收 - [ ] 待讨论 用代表性程序核对上述规则,并解决与其他分类的冲突;没有未解决的阻塞后完成。 ## 当前问题的方案与语言对照 以下沿用原讨论材料;建议和参考语言的做法均不自动成为本语言规则。 当前仍是 **07-01:函数能否作为值**,即能否保存、传参、返回,以及通过变量调用。闭包捕获规则仍留在后续任务。 ### 五种语言怎样做 下面的例子都假定已经定义了一个“输入整数、返回整数”的 `increment` 函数。取舍是针对我们设计的分析,不代表这些语言的历史设计动机。 | 语言 | 规则与具体行为 | 好处、代价及对我们的启发 | |---|---|---| | **Python** | 函数是对象。`f = increment` 保存函数,`f(3)` 调用它;也能放入列表、传参和返回。[官方教程](https://docs.python.org/3/tutorial/controlflow.html#defining-functions) | 行为组合直接;单靠语言运行时的动态检查,部分不合法调用到执行时才发现。**支持函数值,不要求我们采用动态类型或 Python 的对象模型。** | | **JS** | 函数是可调用的对象。例如 `const f = increment; f(3)`,也能作为参数和结果。[ECMAScript 规范](https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-object-type) | 回调与组合方便;接口的正确使用需要额外约束。**函数具有对象属性,是可单独选择的能力,不必随函数值一起引入。** | | **TS** | 延续 JS 的函数值行为,并增加静态函数类型,例如 `const f: (x: number) => number = increment`。类型标注被擦除,实际执行的是 JS,不会自动获得运行时参数校验。[函数类型](https://www.typescriptlang.org/docs/handbook/2/functions.html)、[类型擦除](https://www.typescriptlang.org/docs/handbook/2/basic-types.html#erased-types) | 能提前检查许多接口错误;需要设计类型兼容规则,且静态检查不能代替外部输入验证。**函数值能力和检查时机可以分开决定。** | | **Go** | 支持有静态类型的函数值,例如 `var f func(int) int = increment`,随后 `f(3)`;函数也可传参、返回。函数类型变量的零值为 `nil`。[Go 规范](https://go.dev/ref/spec#Function_types) | 调用接口明确;需要处理空函数值等边界。**可以支持函数值,同时保留较直接的签名模型;我们不必继承 `nil`。** | | **Rust** | 支持保存、传递和返回函数相关的值,但区分函数项、函数指针和闭包。例如 `let f: fn(i32) -> i32 = increment;`。捕获环境的闭包不能直接当作普通 `fn` 指针,通常通过 `Fn` 等接口使用。[Rust 官方书](https://doc.rust-lang.org/book/ch20-04-advanced-functions-and-closures.html)、[函数指针规则](https://doc.rust-lang.org/reference/types/function-pointer.html) | 能明确表达不同调用能力;类型与接口规则更多。**一等函数不要求所有可调用值具有同一种类型或底层表示。** | 这些语言都支持广泛的函数值能力,区别主要在类型、对象能力和环境捕获。**Rust 的类型区分并不意味着它只支持我们下面的 B。** ### 我们当前的三个选项 - **A:函数只作为调用目标。** 可以直接调用,不能作为普通数据保存、传递或返回。规则较少,但通用回调需要其他机制。 - **B:支持受限的函数引用。** 只开放明确的一部分能力,例如仅允许引用静态定义的函数。适合有具体限制目标的语言,需要说明每条限制的理由。 - **C:函数是一等值。** 允许保存、传参和返回,遵守一般的类型与生命周期规则。随后分别决定匿名函数、闭包和函数类型。 **我仍建议 C**:它让排序比较器、事件回调、可替换的处理步骤等接口容易表达,同时保留后续设计空间。 选择 C 后,下面的基础行为应成立: ```text chosen = increment // 保存,不执行 apply(chosen, 3) // apply 内部调用 chosen,得到 4 return increment // 可以返回已有函数 ``` 调用非函数应被拒绝;拒绝发生在静态检查还是运行时,依赖类型系统。返回捕获局部变量的函数是否允许,仍待闭包任务决定。 **你选择 A、B,还是 C?** ## 讨论来源 《语言设计 07:函数与调用》,整理日期 2026-10-28。章节编号沿用原清单。