函数与调用
本文档收敛 Dujie 中函数声明、参数模型、默认值和调用规则。
总体原则
- 函数返回类型必须显式写出
- 第一阶段不做函数返回类型推导
- 第一阶段不把完整“函数类型”纳入公开类型系统
- 第一阶段支持匿名函数
- 匿名函数允许捕获外部变量
- 但匿名函数暂不作为完整一等函数值开放
函数声明
函数声明形式:
func add(x: int, y: int) -> int {
x + y
}
约束:
- 本文当前只收敛顶层函数
- 返回类型必须显式写出
- 普通函数和组件函数共用同一套声明语法
匿名函数
匿名函数语法与普通函数一致,只是没有名字。
例如:
func(x: int) -> string {
`#${x}`
}
当前规则:
- 匿名函数允许捕获外部变量
- 使用词法作用域
- 不需要单独的 capture list
- 匿名函数本身不声明泛型参数
- 主要用于“目标参数类型已知”的回调位置
例如:
let prefix = "item: ";
items.map(func(x: string) -> string {
`${prefix}${x}`
})
第一阶段先不支持:
- 把匿名函数当普通值赋给变量
- 把匿名函数作为普通返回值返回
- 把匿名函数存入
struct字段或容器 - 在匿名函数里修改捕获的外部绑定
参数声明形式
第一阶段支持两种参数模型:
1. 位置参数
func a(x: int, y: opt<string>, z: bool = false) -> widget
规则:
- 参数按顺序声明和传递
- 必填参数必须放在前面
opt<T>参数可放在后面并允许省略- 带默认值的参数必须放在后面并允许省略
2. 具名参数
func b({ x: int, y: opt<string> = none, z: bool = false }) -> widget
规则:
- 只在定义时使用
{ ... }标识具名参数函数 - 调用时不写
{ ... } - 调用时按参数名匹配,顺序无关
第一阶段不支持位置参数和具名参数在同一函数签名中混用。
opt<T> 与参数可省略
参数可选性统一由 opt<T> 表达。
例如:
func button(title: opt<string>) -> widget
这些调用都合法:
button()
button("OK")
button(some("OK"))
button(none)
语义:
button()等价于button(none)- 传入
T时,自动提升为some(T) - 显式传
none时,参数值为空
默认值
默认值规则:
- 默认值必须是编译期可确定表达式
- 默认值暂时不允许依赖前面的参数
- 如果默认值能唯一推导出参数类型,参数类型可以省略
例如:
func a(x = 1, y = "snow") -> unit
func b({ x = 1, y: opt<string> = none }) -> unit
这些不合法:
func a(x: int, y = x) -> int
func b(v = none) -> unit
原因:
- 默认值不能依赖前面的参数
none不能单独推出参数类型,必须显式写成opt<T>
调用规则
位置参数函数
- 实参按顺序匹配
- 尾部
opt<T>参数可省略 - 尾部带默认值参数可省略
- 不允许跳过中间参数
具名参数函数
- 调用形式:
b(x: 1, y: some("snow"))
- 顺序无关
- 未给默认值的非
opt<T>参数必填 opt<T>参数可不传,不传时为none- 带默认值的参数可不传,不传时取默认值
- 未声明参数是编译错误
- 同名参数重复传递是编译错误
返回
返回规则:
return expr;的类型必须与函数声明返回类型一致- 返回
unit的函数允许写return; - 非
unit函数不能写裸return; - 函数体末尾的尾表达式可以作为隐式返回值
- 非
unit函数的所有控制路径都必须返回值
func 的当前边界
第一阶段的 func 只承担“声明和调用函数”的能力。
当前不支持:
- 函数作为普通变量类型使用
- 函数存入
struct字段或容器 - 完整高阶函数类型系统
少数内建场景可以接受具名函数或匿名函数作为回调,例如后续 iter.map / iter.filter。