Proposal: Auto Lambda
XGo already supports a family of "command-style" calls that take a trailing lambda expression, written with an explicit =>:
times 3, => {
... // repeat this block 3 times
}
forEver => {
... // repeat this block forever
}
onKey KeySpace, => {
... // triggered when the space key is pressed
}This works uniformly across all XGo code today. However, in certain classfiles — most notably game/animation-oriented ones such as spx — this pattern is extremely common, and the => { boilerplate adds visual noise to what is conceptually closer to a native control-flow construct (a loop, an event handler, etc.).
We want classfile authors to be able to opt in to a terser form for specific commands, so the same code can instead be written as:
times 3 {
... // repeat this block 3 times
}
forEver {
... // repeat this block forever
}
onKey KeySpace {
... // triggered when the space key is pressed
}We call this feature Auto Lambda: the trailing { ... } block is automatically recognized as a lambda argument, without requiring =>.
Auto Lambda is strictly a parser-level sugar. It introduces no new runtime semantics and no new AST node kinds — it only changes how a specific, explicitly opted-in set of command calls may be spelled.
Non-Goals
- Auto Lambda is not a general replacement for
=>. Plain function calls, and command calls in classfiles that have not enabled Auto Lambda for that command, must continue to use the existingcmd args..., => { ... }form. - Auto Lambda does not change how lambdas are compiled, how closures capture variables, or how command calls are type-checked. It only affects surface syntax recognition during parsing.
- Auto Lambda is scoped per classfile and per command name. It is never a global language change; it cannot be enabled for ordinary (non-classfile) XGo source files.
Activating Auto Lambda
A classfile enables Auto Lambda for a specific set of commands by adding an autolambda directive to its gox.mod:
autolambda times(1), forEver(0), onKey(1)Each entry has the form name(n), where:
nameis the command's identifier (the function/method name as called in XGo source, e.g.times,forEver,onKey).nis the number of non-lambda arguments the command takes, i.e. the arguments that appear before the (now implicit) trailing lambda.
For example:
| Directive entry | Full call form | Meaning |
|---|---|---|
times(1) |
times 3, => { ... } |
1 argument (3) before the lambda |
forEver(0) |
forEver => { ... } |
0 arguments before the lambda |
onKey(1) |
onKey KeySpace, => { ... } |
1 argument (KeySpace) before the lambda |
A single gox.mod may declare Auto Lambda for multiple commands in one directive, as shown above, or across multiple autolambda lines.
Parsing Rules
When the parser is processing source belonging to a classfile whose gox.mod declares autolambda for a command name(n), it applies the following rule while parsing a command-style call to name:
- Parse
namefollowed by exactlyncomma-separated arguments, exactly as it would for a normal command call. - After those
narguments, instead of requiring, => {, the parser also accepts a bare{directly starting a block. - If a bare
{is found in that position, it is parsed as the lambda body and treated exactly as if the source had written, => {(or, whenn == 0,=> {) at that point. - The explicit
=>form remains valid at all times, even for commands that have Auto Lambda enabled. Auto Lambda only adds an alternative spelling; it never removes the original one.
If a command with Auto Lambda enabled is called with a different number of leading arguments than declared (e.g. times called with 2 arguments before a bare {), the bare { is not treated as an implicit lambda; ordinary parsing rules apply, and this will surface as a normal type/argument-count error at the call site, not as an Auto Lambda-specific error. This keeps the feature's parsing rule simple: it only fires when the argument count exactly matches what gox.mod declares.
Why this doesn't need => to disambiguate
Outside of Auto Lambda, => exists so the parser can tell a command call with a trailing lambda apart from a command call statement followed by an unrelated block statement, or from other constructs that could start with {. Because Auto Lambda is enabled per command name, and only inside classfiles that explicitly opt in, the parser already knows — at the moment it starts parsing a call to times, forEver, or onKey in such a classfile — that a bare { immediately following the declared argument count can only mean one thing: the lambda body. There is no ordinary XGo statement form where a command call is immediately followed by a { block that means something else, so no ambiguity is introduced.
Under the Hood
Auto Lambda does not change code generation, type checking, or overload resolution. Once parsed, times 3 { ... } produces the same LambdaExpr node — with the same argument list and body — as times 3, => { ... } would have.
The one AST-level change is a new boolean field on LambdaExpr:
AutoLambda boolWhen the parser desugars a bare { ... } into a LambdaExpr because => was omitted (i.e. the Auto Lambda form was used), it sets AutoLambda to true. When the source uses the explicit => { ... } form, AutoLambda is left as its zero value, false. In both cases the rest of the node (the lambda's parameters and body) is populated identically.
Consequently:
- Type checking, overload resolution, and
goxcode generation do not need to branch onAutoLambdaat all — the field exists purely so that source position/spelling information isn't lost, not because the two forms compile differently. - Tools that consume the XGo AST (formatters, linters, later compiler passes) can use
AutoLambdaif they need to preserve or reproduce the original spelling (for example, a formatter that wants to printtimes 3 { ... }back out instead of expanding it totimes 3, => { ... }), but no compiler stage is required to treat the two forms differently.
Example: spx-style Classfile
gox.mod:
autolambda times(1), forEver(0), onKey(1), onStart(0)Usage:
onStart {
say "Hello, XGo!"
}
forEver {
step 1
}
times 5 {
turn 90
}
onKey KeySpace {
jump
}Note the last call still uses the explicit => form — this remains valid even though onKey has Auto Lambda enabled, since Auto Lambda only adds an alternate, terser spelling rather than replacing the existing one.
Compatibility
- Files outside of any classfile, or classfiles whose
gox.moddoes not declareautolambdafor a given command, are entirely unaffected: such commands must continue to use=>as they do today. - Enabling
autolambdafor a command ingox.modis purely additive and backward compatible — all existing call sites using=>keep compiling unchanged. - Because the argument count
nis declared explicitly ingox.modrather than inferred from a function signature, Auto Lambda works uniformly for overloaded commands, as long as every overload that is meant to support Auto Lambda shares the same leading-argument countn.
Open Questions
Should
gox.modallow declaring Auto Lambda for a command with more than one possible leading-argument count (e.g. supporting bothtimes(1)and a futuretimes(2)overload)? The current proposal assumes a singlenper command name; extending this is left for future work if a concrete use case arises.One possible future direction is a special value
times(-1), meaning "support a variable number of leading arguments" rather than a fixed count. However, this cannot be made to work while also preserving backward compatibility with the explicit=> {form: with a fixedn, the parser knows exactly where the argument list ends and can therefore accept either=>or a bare{at that boundary; with a variadic argument count, the parser has no fixed boundary to look for, so a bare{becomes the only reliable signal that the argument list has ended and the lambda has begun. In other words,times(-1)would make Auto Lambda mandatory for that command — the=> {form could no longer be supported alongside it. Because this is a real forward-compatibility break (not just an additive convenience), it is explicitly out of scope for this proposal and is left as a possible, separately-designed extension.Should IDE tooling (gopls-equivalent) offer a quick-fix to convert between the
=>form and the Auto Lambda form? This is an editor-tooling concern outside the scope of this language proposal.
Source: goplus/xgo