函数与调用

本文档收敛 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