Skip to Content

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));
10

Apply((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 1

tick 每调用一次 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日