Proposal: Fat-Arrow (`=>`) Closures

Proposal: Fat-Arrow (=>) Closures

1. Syntax & Semantics

Mojo adopts => as a structural anchor to support anonymous functions without the lambda keyword or C-style curly braces.

A. Single-Line Form (Implicit Return)

Single-line arrows evaluate directly as expressions and implicitly return their result.

# Single parameter (parentheses optional)
var double = x => x * 2

# Typed multi-parameter
var multiply = (a: Int, b: Int) -> Int => a * b

B. Multi-Line Form (Explicit return Required)

Triggered when => is followed immediately by a newline (\n). Multi-line closure blocks require an explicit return statement.

var results = numbers.map(x =>
    var square = x * x
    var cube = square * x
    return square + cube  # Explicit return required
)

C. Positional Arguments & Baseline Commas

Argument commas following a multi-line closure must sit at or outside the parameter baseline (on a new line) to dedent the block properly:

async_dispatch(
    on_success = data =>
        var clean = sanitize(data)
        return process(clean)
    ,  # Baseline comma exits block context and separates arguments
    on_error = err =>
        return log(err)
)

2. Compiler Architecture

  • Cover Grammar: The parser parses candidate parameter lists (...) as standard parenthesized expressions and promotes them to closure heads only when encountering =>.

  • Dual Lexer Stacks: Line-break suppression inside parentheses is managed using two separate lexer stacks:

    1. A Parenthetical Nesting Stack tracking (), [], {} depth.

    2. An Indentation Baseline Stack tracking column positions for active => \n blocks to trigger INDENT and DEDENT tokens inside argument lists.

3. Design Trade-Offs

  • Pros: 100% Pythonic aesthetic, eliminates lambda, enables multi-line inline logic without brace syntax.

  • Cons: Floating baseline commas , are required when passing multi-line closures as non-final positional arguments. APIs will naturally favor trailing closures or keyword parameters.

If this is to be adopted, ideally we should remove lambda syntax completely. Otherwise we have two ways of doing the same thing and we lose on consistency.

P.S. we can have last expression as return type instead of explicit return type - this is common in other light weight closure syntax. Though we lose on explicitness.

I think we will have to keep lambda for backwards compatibility.
Because of the complexity of implementing the feature, it will be post 1.0.
The feature requires significant changes to the lexer and parser.

The advantage of lambda is that it’s context-free.
This feature requires either unbounded token lookahead or a cover grammar.

Both designs have tradeoffs. Unbounded lexer lookahead requires either an unbounded token buffer/deque or lexer rewinding support.
A cover grammar would require support for temporary parser nodes and reinterpreting parser nodes. I am not familiar with the Mojo parser, so I can only speculate how complicated it would be to support this feature with a parser that performs type checking.

While the lambda syntax would not be as popular, it would still make Mojo more Pythonic and help Python programmers understand closures.

So, should return be required, I think yes.

I am not arguing with symmetry, though this could be an argument made.

Rather, I am arguing with the complexity of Python’s indent/dedent escaping. Arrow functions would be the only production that could escape the suppression. Thus, I think it would be good to have return as a visual stopping token to switch between indent/dedent suppression and non-suppression.