lambda 与闭包(Closure)
lambda 是匿名函数值。写法分简短与完整两族,捕获外层局部变量后就成了闭包。需要给回调、一次性小函数一个不需要起名字的写法时可以考虑使用它。
两种形态
(a, b) => expr // 简短:表达式体
(x: T) => { stmt; } // 简短:块体
func(x: T) -> U => expr // 完整:func 关键字 + 返回注解- 参数表必须带括号,没有单参免括号的形态;
- 参数类型可注解可推断,块体的返回类型推断自 return;
- 完整形态能带子句:
func(x: T) -> U uses IO => expr、failable。
val add = (a: int, b: int) => a + b;
val classify = (n: int) =>
{
if n >= 0
{
return "非负";
}
return "负";
};
val hello = () => "你好";
print(add(2, 3), classify(-1), hello());5 负 你好定义之后立刻调用也合法,括号包住 lambda 再跟实参:
print(((n: int) => n * 2)(21));42函数类型
lambda 的类型是函数类型,两种写法等价:
(int, int) -> int // 简短形式
func(int, int) -> int // 显式形式注解位、参数位、返回位都可用。高阶函数把函数类型写进签名:
func Apply(f: func(int) -> int, n: int) -> int => f(n);
print(Apply((x) => x * 2, 5));10Apply((x: int) => x + 1, 9) 直接把 lambda 当实参传入。
闭包捕获
lambda 体里可以直接读外层局部变量,捕获的是变量本体,不是当时的值:外层改动后续可见,lambda 内的写也持久存活:
func MakeCounter() -> func() -> int
{
var n = 0;
return () =>
{
n += 1;
return n;
};
}
val tick = MakeCounter();
val tick2 = MakeCounter();
print(tick(), tick(), tick(), tick2());1 2 3 1tick 每调用一次 n 递增 1,三次得 1、2、3。tick2 是另一次 MakeCounter() 的产物,有自己的 n,从 1 起步。闭包让「带状态的小行为」不需要先写一个类。
与具名函数值的区别
具名函数名在值位也物化为函数值,但它与 lambda 的身份规则不同:
- 同声明的具名函数 = 同一个值(单例):
val a = On; val b = On;是同一个函数值; - lambda 逐次求值生成新实例:同一个 lambda 表达式写两次,得到两个不相等的值,各自的闭包环境独立。
需要按身份去重的场合(比如事件订阅的退订匹配)用具名函数,见事件部分。== 就按实例身份判:具名单例恒等,逐次求值的 lambda 互不等,见判等与哈希。
failable lambda
完整 lambda 可以带 fail 子句,fail E 写在 -> 之前或之后都合法;体内 fail 语句收集进这个函数值自己的失败契约:
error Bad(n: int);
val risky = func(x: int) fail Bad -> int
{
if x < 0
{
fail new Bad(x);
}
return x;
};
print(try? risky(5));
print(try? risky(-1));5
null调用它同样受消费义务约束:try? risky(5) 得 5,try? risky(-1) 得 null。把 fail 集更宽的 lambda 传给契约更窄的槽位会在编译期被拒绝。
注意
async (x) => expr是异步 lambda,返回task<T>,见异步部分;- lambda 内捕获
self的规则与类型成员相关,见类型部分;
最后更新于 2026年10月9日
