I was skimming through this and the "old" pitch thread and figured maybe I should provide some clarification for some of the Scala callouts made throughout the threads. Since it's a prominent example of the last-expression-as-return feature and some have mentioned it causing unexpected problems; I've spent close to a decade with Scala code and was consulting developers of various skill level back then in their Scala usage.
I'll introduce the "known problems with this" in Scala and compare if we'd have the same problem in Swift or not:
--
The "gotcha's" in Scala:
The implicit return had two (or three I guess) very specific problems:
- unexpected return from outer scope from closures
Which produces:
WARNING: * Non local returns are no longer supported; use boundary
and boundary.break
in scala.util
instead
- unexpected inferred function return type
Scala 2 (old):
Which leads to unexpected inferred types. The problem honestly only shows up as real issue at function boundaries because when you write smaller functions and ifs you rarely end up with weird types inferred. (Scala 3 solves this by introducing union types: in the above example the return type in Scala 3 is Int | String
.
- (a) is a similar problem where randomly throwing in an early
return
can make inferred return types confusing... So nowadays doing this is even triggered as an error:
ERROR: method fun has return statement; needs result type
Nowadays both are either resolved or banned in Scala.
So... does this proposal lead to the same issues in Swift?
IMHO: No, it does not.
return
in Swift behaves in expected ways, returning from whatever scope it was in;
- Swift methods cannot omit return types and they must be spelled out.
I believe this proposal is a good positive addition, and solves issues that we have today:
- early returns look easy to spot because the return keyword is a clear "things happening here!"
- early returns are often paired with
guard
- I think it is good to keep guard+return
pair. we signal that it's an early return.
- unexpected return types getting inferred in methods is not a problem because Swift requires explicit return type annotation (this is good)
- one might argue that it happens in closures; however closures are supposed to be small pieces of code -- if your closure is getting "huuuuuge" this is another good reason to make a method for it.
Overall I'm in favor of this because it rounds out the language.
Currently we're in a messy state and the "single line branch body can be used as expression but multiple not" is a confusing cliff in the language which breaks normal programming practices like introducing a temporary variable or even just breaking up a few calls that could be mashed into one horrible too-many-characters long line, and encourages writing clear code.
At least in my experience: In my 6~7 years of Scala use but also lots of teaching and consulting newcomers to Scala way back then before my days with Swift this specific part of the language wasn't really the difficult part and except those gotchas (which Swift won't suffer from), it has made code much less ceremonial and clear.
So, at least on a personal note, I think this is a very valuable improvement that makes Swift more clear to read and I personally look forward to it! I've been hitting the "eh, I need another line in this if" far too often recently... 