# 函数与调用

> 

本文档收敛 Dujie 中函数声明、参数模型、默认值和调用规则。

## 总体原则

- 函数返回类型必须显式写出
- 第一阶段不做函数返回类型推导
- 第一阶段不把完整“函数类型”纳入公开类型系统
- 第一阶段支持匿名函数
- 匿名函数允许捕获外部变量
- 但匿名函数暂不作为完整一等函数值开放

## 函数声明

函数声明形式：

```dj
func add(x: int, y: int) -> int {
    x + y
}
```

约束：

- 本文当前只收敛顶层函数
- 返回类型必须显式写出
- 普通函数和组件函数共用同一套声明语法

## 匿名函数

匿名函数语法与普通函数一致，只是没有名字。

例如：

```dj
func(x: int) -> string {
    `#${x}`
}
```

当前规则：

- 匿名函数允许捕获外部变量
- 使用词法作用域
- 不需要单独的 capture list
- 匿名函数本身不声明泛型参数
- 主要用于“目标参数类型已知”的回调位置

例如：

```dj
let prefix = "item: ";

items.map(func(x: string) -> string {
    `${prefix}${x}`
})
```

第一阶段先不支持：

- 把匿名函数当普通值赋给变量
- 把匿名函数作为普通返回值返回
- 把匿名函数存入 `struct` 字段或容器
- 在匿名函数里修改捕获的外部绑定

## 参数声明形式

第一阶段支持两种参数模型：

### 1. 位置参数

```dj
func a(x: int, y: opt<string>, z: bool = false) -> widget
```

规则：

- 参数按顺序声明和传递
- 必填参数必须放在前面
- `opt<T>` 参数可放在后面并允许省略
- 带默认值的参数必须放在后面并允许省略

### 2. 具名参数

```dj
func b({ x: int, y: opt<string> = none, z: bool = false }) -> widget
```

规则：

- 只在定义时使用 `{ ... }` 标识具名参数函数
- 调用时不写 `{ ... }`
- 调用时按参数名匹配，顺序无关

第一阶段不支持位置参数和具名参数在同一函数签名中混用。

## `opt<T>` 与参数可省略

参数可选性统一由 `opt<T>` 表达。

例如：

```dj
func button(title: opt<string>) -> widget
```

这些调用都合法：

```dj
button()
button("OK")
button(some("OK"))
button(none)
```

语义：

- `button()` 等价于 `button(none)`
- 传入 `T` 时，自动提升为 `some(T)`
- 显式传 `none` 时，参数值为空

## 默认值

默认值规则：

- 默认值必须是编译期可确定表达式
- 默认值暂时不允许依赖前面的参数
- 如果默认值能唯一推导出参数类型，参数类型可以省略

例如：

```dj
func a(x = 1, y = "snow") -> unit
func b({ x = 1, y: opt<string> = none }) -> unit
```

这些不合法：

```dj
func a(x: int, y = x) -> int
func b(v = none) -> unit
```

原因：

- 默认值不能依赖前面的参数
- `none` 不能单独推出参数类型，必须显式写成 `opt<T>`

## 调用规则

### 位置参数函数

- 实参按顺序匹配
- 尾部 `opt<T>` 参数可省略
- 尾部带默认值参数可省略
- 不允许跳过中间参数

### 具名参数函数

- 调用形式：

```dj
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`。
