Featured image of post Go 的 Lambda 之争:一个提案争论了 8 年

Go 的 Lambda 之争:一个提案争论了 8 年

 

Go 的 Lambda 之争:一个提案争论了 8 年

一个提案,900 条评论,Go 核心团队亲自下场——这场争论终于要尘埃落定了吗?

我们在用 Go 写项目的时候,一定写过这样的代码:给 sort.Slice 传一个比较函数。每次都要把参数类型写完整,即使编译器早就知道它们是什么类型。这种重复且无法省略的语法噪音,相信让不少开发者感到困扰。

2017 年 8 月,有人在 Go 仓库提了一个 issue:#21498 — proposal: spec: short function literals。没人预料到,这个看似"小修小补"的提案会变成 Go 历史上讨论最激烈的语法辩论——持续 8 年,累计 900+ 条评论,至今仍未关闭。

今天,我们来聊聊这场争论的来龙去脉,以及它为什么如此难以尘埃落定。

问题在哪里?

让我们先看一段再熟悉不过的代码——用 sort.Slice 对切片排序:

1
2
3
sort.Slice(people, func(i, j int) bool {
    return people[i].Age < people[j].Age
})

这里有个问题:ij 的类型,sort.Slice 的函数签名里已经写得很清楚了:func(i, j int) bool。编译器完全有能力推断出来,但 Go 强制我们把类型再写一遍

这在其他支持 Lambda 的语言里,可以简洁得多:

1
2
// 理想中的 Go
sort.Slice(people, (i, j) => people[i].Age < people[j].Age)

有人可能会说:“多写几个字符而已,有什么大不了?”

但问题在于,Go 1.18 引入泛型之后,高阶函数的使用场景激增。slices.SortFuncmaps.Keys、迭代器适配器……冗长的函数字面量成了每天都要面对的摩擦,而且无法规避。

八年争论的三个阶段

第一阶段(2017–2020):我们真的需要这个吗?

提案刚提出时,社区反应相当冷淡。

Go 核心成员 Dave Cheney 直接表态:“Please no, clear is better than clever.”

这体现了 Go 的一贯哲学——显式优于隐式。编译器能推断类型,不代表就应该让它推断。代码是写给人看的,清晰比简洁更重要。

这个阶段的争论核心其实是:Go 的简洁哲学,边界在哪里?

第二阶段(2020–2023):语法方案大爆炸

2022 年 Go 1.18 泛型落地后,情况发生了变化。

高阶函数的使用场景爆发式增长,函数字面量的"啰嗦"成了真实的痛点。社区开始积极提案,短短两年内出现了几十种语法变体

1
2
3
4
5
6
7
8
// 箭头语法(JavaScript 风格)
(x, y) => x < y

// Haskell 风格
\(x, y) x < y

// fn 关键字(Rust 风格)
fn(x, y) { return x < y }

每种方案都有拥护者,也都有反对者。有人总结得很精准:“每 50 条评论就会有人重新提一遍同样的语法。”

第三阶段(2024 至今):终于开始收敛

2024 年 6 月,Robert Griesemer 牵头成立了一个小型工作组,试图终结这场争论。他明确了三个约束条件:

  1. 核心收益是省略参数的类型注解
  2. 参数必须用某种括号包裹
  3. 不能把参数移到函数体内部(避免破坏现有代码结构)

在这些约束下,目前呼声最高的方案是 fn 关键字语法:

1
2
3
4
5
// fn 关键字 + 类型推断
sort.Slice(people, fn(i, j) bool { return people[i].Age < people[j].Age })

// 表达式体(最简写法)
sort.Slice(people, fn(i, j) => people[i].Age < people[j].Age)

为什么这么难决定?

很多人可能会问:不就是一个语法糖吗?为什么能争论 8 年?

这里涉及一个根本性的张力:简洁到什么程度会变成晦涩?

Go 的简洁不是偶然的。每一个语法特性的加入都有真实的代价

  • 学习成本:新开发者需要理解的语法规则 +1
  • 维护成本:语言规范需要覆盖的边界情况 +N
  • 工具成本:编译器、IDE、静态分析工具都要适配

让我用一个具体的例子来说明这个张力。Go 1.23 引入了迭代器(iterator),这是泛型之后最重要的特性之一。看看下面这段代码:

1
2
3
4
5
6
7
8
// 现在的 Go(真实可运行代码)
for k, v := range slices.Sorted(func(yield func(string, int) bool) {
    for k, v := range m {
        yield(k, v)
    }
}) {
    fmt.Println(k, v)
}

如果引入短函数字面量,可以写成:

1
2
3
4
5
for k, v := range slices.Sorted(fn(yield) {
    for k, v := range m { yield(k, v) }
}) {
    fmt.Println(k, v)
}

第二种更短。但对刚入门的开发者来说,它更清晰吗?

Go 社区的核心分歧就在这里:一派认为简洁本身就是清晰,另一派认为显式才是 Go 的灵魂

接下来会怎样

截至 2026 年初,该提案尚未正式接受,但反对派已经输掉了核心争论

泛型 + 迭代器 + 函数式模式的组合,让"啰嗦的函数字面量"成为真实的人体工学问题。现在的问题不是要不要解决,而是怎么解决。

如果 Go 1.27 带着短函数字面量发布,这将是继泛型以来最重要的语法变化。八年的争论,也许就浓缩在几个字符里。

总结

  1. Issue #21498 自 2017 年开放,900+ 条评论,Go 史上最长语法辩论
  2. 核心问题:编译器能推断类型时,Go 仍然强制显式标注
  3. 社区正在向 fn(params)\(params) 收敛
  4. Go 团队的约束:简洁不能以牺牲可读性为代价
  5. 正式提案可能在 2026 年落地,持续关注 issue #21498

  • 本文长期链接
  • 如果您觉得我的博客对你有帮助,请通过 RSS订阅我。
  • 或者在X上关注我。
  • 如果您有Medium账号,能给我个关注嘛?我的文章第一时间都会发布在Medium。
Licensed under CC BY-NC-SA 4.0
最后更新于 Mar 04, 2026 21:46 CST
使用 Hugo 构建
主题 StackJimmy 设计