-
Notifications
You must be signed in to change notification settings - Fork 29
SIP-75: Allow single-line lambda after :
#118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
odersky
wants to merge
8
commits into
scala:main
Choose a base branch
from
odersky:single-line-lambdas
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 2 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
bd05b69
SIP-XX: Allow single-line lambda after `:`
odersky 777db11
Six pre-sip thread URL
odersky ee2f8b1
Update content/singleLineLambdas.md
odersky f656255
Admit curried parameter sections in multi-line lambdas
odersky 3f77056
Apply suggestion from @prolativ
odersky 2e6bb89
Apply suggestion from @prolativ
odersky fc60d3f
Apply suggestion from @prolativ
odersky 021d107
Discussion of nested single-line lambdas
odersky File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
--- | ||
layout: sip | ||
permalink: /sips/into.html | ||
stage: design | ||
presip-thread: https://contributors.scala-lang.org/t/pre-sip-allow-single-line-lambdas-after/7258 | ||
title: Allow single-line lambdas after `:` | ||
--- | ||
|
||
**By: Martin Odersky** | ||
|
||
## History | ||
|
||
| Date | Version | | ||
|---------------|--------------------| | ||
| Sep 21, 2025 | Initial Draft | | ||
|
||
## Summary | ||
|
||
This proposal is to allow a lambda expression following a `:` on the same line. | ||
Currently, we need a newline and indent after the arrow, e.g. | ||
```scala | ||
xs.map: x => | ||
x + 1 | ||
``` | ||
We propose to also allow to write the lambda on a single line: | ||
```scala | ||
xs.map: x => x + 1 | ||
``` | ||
|
||
The lambda extends in this case to the end of the line. | ||
|
||
## History | ||
This feature has been demanded repeatedly since the colon-lambda syntax was introduced as part of [SIP 44](https://docs.scala-lang.org/sips/fewer-braces.html), for instance see a [recent thread in Scala Users](https://users.scala-lang.org/t/why-were-single-line-lambdas-removed/11980/6). The original SIP 44 did not include it, because the concern at the time was the feature as a whole would look too much like type ascription and single line lambdas after colon would make that worse. But the experience since SIP 44 shipped has shown that the concerns about confusion with type ascriptions were largely overblown. So we now come back to the issue in a separate SIP. | ||
|
||
## Motivation | ||
|
||
The new behavior is more general and more intuitive. We can now state that a `:` means application if it is followed by an indented block or by a lambda. | ||
|
||
The new behavior also makes refactoring easier. One often splits or combines lines when some code part changes in length. We can now do this for lambda arguments without having to switch between parentheses and `:`. | ||
|
||
## Other Examples | ||
|
||
The syntax works for all kinds of function literals. They can start with one or more parameters, or with type parameters, or they can be partial functions starting | ||
with `case`. | ||
|
||
```scala | ||
Seq((1, 2), (3, 4)).map: (a, b) => a + b | ||
|
||
Seq((1, 2), (3, 4)).map: (a: Int, b: Int) => a + b | ||
|
||
Seq((1, 2), (3, 4)).collect: case (a, b) if b > 2 = a | ||
|
||
(1, true).map: [T] => (x: T) => List(x) | ||
``` | ||
|
||
## Detailed Spec | ||
|
||
A `:` means application if its is followed by one of the following: | ||
|
||
1. a line end and an indented block, | ||
2. a parameter section, followed by `=>`, a line end and an indented block, | ||
3. a parameter section, followed by `=>` and an expression on a single line, | ||
4. a case clause, representing a single-case partial function. | ||
|
||
(1) and (2) is the status quo, (3) and (4) are new. | ||
|
||
**Restriction:** (3) and (4) do not apply in code that is immediately enclosed in parentheses (without being more closely enclosed in braces or indentation). This is to avoid an ambiguity with type ascription. For instance, | ||
```scala | ||
( | ||
x: Int => Int | ||
) | ||
``` | ||
still means type ascription, no interpretation as function application is attempted. | ||
|
||
## Compatibility | ||
|
||
Because of the restriction mentioned above, the new scheme is fully compatible with | ||
existing code. This is because type ascriptions with function types are currently only allowed when they are enclosed in parentheses. | ||
|
||
The scheme is also already compatible with _SIP-XX - No-Curly Partial Functions and Matches_ since it allows case clauses after `:`, so single case clauses can appear syntactically in all contexts where lambdas can appear. In fact, one could envisage to merge the two SIPs into one. | ||
|
||
## Implementation | ||
|
||
An [implementation](https://github.com/scala/scala3/pull/23821) of the new rules supports this SIP as well as _SIP-XX - No-Curly Partial Functions and Matches_. The new behavior is enabled by a language import `language.experimental.relaxedLambdas`. | ||
|
||
The implementation is quite straightforward. It does require a rich model of interaction between lexer and parser, but that model is already in place to support other constructs. The model is as follows: | ||
|
||
In the Scala compiler, the lexer produces a stream of tokens that the parser consumes. The lexer can be seen as a pushdown automaton that maintains a stack of regions that record the environment of the current lexeme: whether it is enclosed in parentheses, brackets or braces, whether it is an indented block, or whether it is in the pattern of a case clause. There is a backchannel of information from parser to scanner where the parser can push a region on the stack. | ||
|
||
With the new scheme we need to enter a "single-line-lambda" region after a `:`, provided the `:` is followed by something that looks like a parameter section and a `=>`. Testing this condition can involve unlimited lookahead when a pair of matching parentheses enclosing a parameter section needs to be identified. If the test is positive, the parser instructs the lexer to create a new region representing a single line lambda. The region ends at the end of the line. | ||
odersky marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
## Syntax Changes | ||
|
||
``` | ||
ColonArgument ::= colon [LambdaStart] | ||
indent (CaseClauses | Block) outdent | ||
| colon LambdaStart expr ENDlambda | ||
| colon ExprCaseClause | ||
LambdaStart ::= FunParams (‘=>’ | ‘?=>’) | ||
| TypTypeParamClause ‘=>’``` | ||
``` | ||
The second and third alternatives of `ColonArgument` are new, the rest is as before. | ||
|
||
Notes: | ||
|
||
- Lexer inserts ENDlambda at the next EOL, before producing a NEWLINE. | ||
- The case does not apply if the directly enclosing region is bounded by parentheses `(` ... `)`. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.