go-lambdas-medium

 

Go Is Finally Getting Lambdas — 8 Years in the Making

How a Single Proposal with 900+ Comments is Quietly Reshaping Go’s Syntax

Medium cover

It started in August 2017. A developer opened issue #21498 on the Go repository with a modest title: "proposal: spec: short function literals." Nobody could have predicted that eight years later, the discussion would stretch to 915 comments, involve Go’s core team, and still be searching for a final answer.

But as of early 2026, we’re closer than ever.

What’s the Problem?

Go’s function literals are verbose. When you pass a callback to a function, you write this:

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

In languages with lambda syntax, the same intent takes far less ceremony:

1
2
// What Go developers dream of
sort.Slice(people, (i, j) => people[i].Age < people[j].Age)

The types of i and j are already known from sort.Slice’s signature. The compiler can infer them. Yet Go forces you to spell them out every single time.

This isn’t just an aesthetic complaint. In modern Go code with generics, higher-order functions, and functional patterns — callbacks appear everywhere. The verbosity adds up.

Eight Years of Debate

The proposal has gone through three distinct phases.

Phase 1 (2017–2020): “Do we even need this?”

The early reaction was skeptical. Dave Cheney captured the sentiment bluntly: “Please no, clear is better than clever.” The Go philosophy has always favored explicitness. If the compiler can infer the types, should it?

Robert Griesemer (one of Go’s original designers) was sympathetic but unconvinced by the early syntax proposals. Ian Lance Taylor suggested restricting it to expression bodies only — no block, no explicit return — which made the tradeoff clearer.

Phase 2 (2020–2023): Syntax proliferation

Once generics arrived in Go 1.18, the callback problem became undeniable. Functional utilities like slices.SortFunc, maps.Keys, and iterator adapters made verbose function literals a daily friction point.

Dozens of syntax variants were proposed:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// Variant A: arrow syntax
(x, y) => x < y

// Variant B: backslash (inspired by Haskell)
\(x, y) x < y

// Variant C: fn keyword
fn(x, y) { return x < y }

// Variant D: dot notation
.(x, y) { return x < y }

Each had vocal supporters and equally vocal critics. The thread became, as one commenter put it, “an infinite loop with a new syntax proposed every 50 comments.”

Phase 3 (2024–present): Convergence

In June 2024, a smaller working group reached a narrow consensus. The key insight from Griesemer crystallized the requirements:

  • The primary benefit is leaving type annotations away in the signature
  • Parameters should be enclosed in some form of bracketing
  • Moving parameters inside the block is a non-starter

The current frontrunner syntax:

1
2
3
4
5
// fn keyword with type inference
sort.Slice(people, fn(i, j) bool { return people[i].Age < people[j].Age })

// Or with expression body
sort.Slice(people, fn(i, j) => people[i].Age < people[j].Age)

The \() variant — inspired by Haskell’s lambda — also has strong support for its brevity, though readability concerns persist.

Why This Is Hard

Go’s simplicity is not accidental. Every feature addition carries a cost: cognitive load for new developers, edge cases in the spec, tooling complexity, and the risk of inconsistency with existing idioms.

The short function literal proposal exposes a genuine tension. The Go team must answer: at what point does brevity become obscurity?

Consider this real-world scenario with iterators (new in Go 1.23):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Current 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)
}

// With short function literals
for k, v := range slices.Sorted(fn(yield) {
    for k, v := range m { yield(k, v) }
}) {
    fmt.Println(k, v)
}

The second version is shorter. But is it clearer? That depends entirely on whether Go developers internalize the new syntax — which takes years at the ecosystem level.

What Happens Next

The proposal is not yet accepted. As of early 2026, the Go team is still evaluating the exact syntax. The most likely outcome is a conservative version: type inference for parameters, optional expression body, familiar bracketing.

What’s clear is that the “no” camp has lost the argument. The combination of generics, iterators, and functional patterns has made verbose function literals a genuine ergonomic problem. The question is now how to solve it, not whether to.

If Go 1.27 ships with short function literals, it will be one of the most significant syntax additions since generics. Eight years of debate, distilled into a few characters.

Summary

  1. Issue #21498 has been open since 2017, with 900+ comments — Go’s longest-running syntax debate
  2. The core problem: Go requires explicit types in function literals even when they can be inferred
  3. After years of proposals, the community is converging on fn(params) or \(params) syntax
  4. The Go team’s constraint: brevity must not come at the cost of readability and learnability
  5. A formal proposal is likely in 2026 — watch the issue

If this post helped you understand Go’s evolution, consider following for more deep dives into Go internals and language design.


  • Long Time Link
  • If you find my blog helpful, please subscribe to me via RSS
  • Or follow me on X
  • If you have a Medium account, follow me there. My articles will be published there as soon as possible.
Licensed under CC BY-NC-SA 4.0
Last updated on Mar 04, 2026 12:58 CST
Built with Hugo
Theme Stack designed by Jimmy