Before, logically. Both because it makes sense and because consider the situation:
let result = if condition { x } else {
var tmp = y * z
then tmp
tmp *= 2
print("\(tmp)")
}
result
ends up being y * z
. It's just following the existing rules regarding how variables and program state works across a sequence of statements. You can think of it as equivalent to result = tmp
in this case.
If the value in question is a reference type, like a class instance, then of course mutations in subsequent statements would be visible, just as they would in any other comparable situation.
Now, I'm not sure why you'd want to write that sort of thing, generally, but maybe there's the occasional case where it just makes sense for various contextual reasons. e.g. maybe for debugging or experimentation purposes you want to short-cut a series of steps and temporarily use the intermediary result.
You could technically do it differently; you could make then
'designate' the return expression such that the expression evaluates to the value of that expression at the end of the block, but I think that's both more complicated to reason about and to implement.
I want to stress that this is somewhat academic - I don't expect folks will use multi-statement expressions for complicated things, or at least I hope they don't. It's merely important that the syntax & rules involved be consistent with the rest of the language and well-defined. Best not compound a dubious design with unusual behaviour.
I concur, because it's not what I was suggesting.
return
is very different to then
. That's really the whole point.
return
is final within a function; you have no need to reference what was returned after it because [within that block] nothing runs after it¹. If you care about what's returned you can capture that externally, as the return value from the function, or refactor your code to name the returned value by storing it into a variable.
then
is (as I'm proposing) not a 'final' statement because it's not a control flow statement. It's simply designating the return value of the expression, which can logically be done anywhere in the block. Execution continues normally to the next statement.
It does harken back a little to Pascal, as (I think it was) @tera pointed out earlier, which demonstrated that return values and returning can be implemented as orthogonal features within a programming language. That was in fact the convention in the early days, because when it comes down to it most CPU architectures actually work that way. Even today.
I'm not proposing that for Swift, in the sense of changing how return
works. Rather, I'm suggesting there is utility in adopting that (in a limited sense) in this new context of multi-statement expressions.
¹ defer
does run after return
yet within the same function context, but today can't implicitly reference the returned value. There is indeed that separate discussion about whether defer
might be useful for capturing function exit results (whether return values or thrown exceptions), but that's not really relevant here for a variety of reasons:
- It's for function results, not expression results.
- I'm not sure that's a good idea in general, given
defer
's purpose.
- I don't think it's even the most elegant way to implement decorator macros, the case for which it was invented.