This example is a bit misleading, because you are essentially starting from a position where the workaround is already applied.
There is a big difference, however: if we were to start with this proposal, and the author wrote
let result = if condition {
3
} else {
4
}
then, in the case when the user adds an additional statement above the 3, the compiler has what it needs to produce a fixit, rewriting their code in the DI-based form. Without this feature, there is no hook to introduce this to the user. They must just know it.
I disagree with this framing, as I believe even if the final state was that only single expressions in if
were supported, a brace would be the correct spelling. A brand new syntax seems entirely unnecessary, just another syntax to learn. This was already the conclusion reached in SE-0255. In that case, return-less functions are limited to a single expression, but a new syntax for that form was rejected.
Choosing a brand new single-expression-only syntax like if p => e1 else e2
or var x: Int => e
on the other hand would close off avenues, suggesting that a multi-statement expression syntax was never coming. Or if it did, would land us with an oddly duplicative syntax.
So this leads to the question: must deciding on multi-statement expressions be a precondition of this proposal (despite the precedent of 0255).
Let’s run through the options for multi-statement expressions. I think this list is comprehensive of all the coherent approaches seen so far:
-
Use
return
to mean “make this expression the block’s value”. I think we need to rule this one out as definitively a bad direction. It is already a problem that closure-based control flow likeforEach { }
or5.times { }
lead to confusion betweenreturn
meaningcontinue
. Repurposingreturn
specifically withinif
orswitch
would make that worse. And it conflicts with the goal of allowing explicit function returns. -
Introduce a new keyword instead of repurposing
return
. This is the path Java chose (though their choice –yield
– conflicts with another potential use in Swift). This seems too heavyweight a solution to me, to introduce a whole keyword just for this purpose. I admit I don’t have a good argument for this other than that “adding a keyword for syntactic sugar” seems self-defeating. -
Adopt the “last expression” rule, the choice of Ruby and Rust. The big win here is it solves not just
if
but also extends SE-0255. But I see that part as a bug rather than a feature. Again this is a feels-based argument but whenever I see a function in Rust end with a baretrue
ornil
orresult
I find it very unsettling. I much prefer Swift’s current requirement of an explicit return. -
An idea that @Joe_Groff put out there, I believe as a joke that I then started to take seriously, is a variant of the “last expression” rule where you must join the expressions with
;
. Soif p { log(“true branch”); 3 } else { 4 }
would be allowed, as wouldfunc f() { log(“f”); 3 . This is more explicit than 3), but re-purposes
;` to give it more meaning, which is maybe not a good idea.
I think 1-4 are all not great solutions to the problem we face today with this proposal. Maybe I am in the minority regarding disliking 3, but it would undeniably be a big change for the language that I’m reluctant to rush into purely for the purposes of solving multi-statement if
expressions.
This leads to what I think might be a plausible path forward:
- The last expression is the value of the branch, but only within
if
orswitch
expression branches. This is essentially option 3), but not for function returns, which would still require an explicitreturn
keyword when longer than 1 expression.
The discomfort I feel about implicit function return doesn’t apply here. Making an if
an expression, by putting it on the right-hand side of an assignment or an explicit return
, is explicit enough for my liking. I also suspect it will in nearly all cases be used for short expressions where it’s clear the if
is an expression, whereas my experience looking at rust code is that it can be common to have relatively long functions that just end in an expression.
This also leaves the option of expanding implicit return of SE-0255 open by implementing 3), without requiring that direction.
There is an implementation, along with a tool chain to try this out, available on this PR.