Switch expressions -- some revisions

Brian Goetz brian.goetz at oracle.com
Tue Dec 19 19:40:50 UTC 2017

I've updated the JEP to reflect these proposed changes:


On 12/14/2017 4:22 PM, Brian Goetz wrote:
> After reviewing the feedback on the proposal for switch expressions, 
> and a bit of going back to the drawing board, I have some proposed 
> changes to the plan outlined in the JEP.
> 1.  Throw expressions.  While throw expressions are a reasonable 
> feature, many expressed concern that if permitted too broadly (such as 
> in method invocation context), they would encourage "tricky" code for 
> little incremental expressiveness.  The real need here is for arms of 
> expression switches to be able to throw when an unexpected state is 
> encountered; secondarily it may be useful allow a value-bearing lambda 
> to unconditionally throw as well.  But extending this to &&, ||, 
> assignment, and method invocation context seems like asking for 
> trouble.  So we'll narrow the treatment here, allowing throw on the 
> RHS of a switch expression ARM, and possibly also the RHS of a lambda. 
> (This doesn't close any doors on making `throw` an expression later, 
> if desired.)
> 2.  Local return from switch.  In the proposal, we borrowed the 
> convention from lambda to use "return" for nonlocal return, mostly on 
> the theory of "follow the arrow".  But this is pretty uncomfortable, 
> made worse by several factors: a) despite the syntactic similarity, we 
> don't follow exactly the same rules for case arms of expression 
> switches as for lambdas (such as treatment of captured vars), and b) 
> when refactoring from statement switch to expression switch or vice 
> versa, there's a danger that an existing "return" could silently swap 
> between nonlocal and local return semantics.
> So we dusted off an old idea, which we'd previously explored but which 
> had some challenges, which is to use "break" with an operand instead 
> of "return" to indicate local return in switch expressions.  So:
>     int y = switch(abs(x)) {
>         case 1 -> 1;
>         case 2 -> 2;
>         case 3 -> 3;
>         default -> {
>             println("bigger than 3");
>             break x;
>         }
>     };
> The challenge is ambiguity; this could be interpreted as a nonlocal 
> break out of an enclosing loop whose label is `x`.  But then we 
> realized that if `x` is both a variable and a label, we can just 
> reject this, and tell the user to rename one or the other; since 
> alpha-renaming the label is always source- and binary-compatible, the 
> user has at least one (if not two) reasonable choices to get out of 
> this problem.
> The benefit here is that now "break" means basically the same thing in 
> an expression switch as it does in a statement switch; it terminates 
> evaluation of the switch, providing a value if one is needed.  Having 
> addressed the ambiguity problem, I think this is a slam-dunk, as it 
> aligns expression switch and statement switch quite a bit (same 
> capture rules, same control flow statements.) We can also, if we like, 
> support "break" for local return in lambdas (we should have done this 
> in 8), to align the two.
> 3.  (Optional.)  There's room to take (2) farther if we want, which is 
> to complete the transformation by eliminating the fake "block 
> expression" in favor of something more like existing switch.  The idea 
> would be to borrow from statement switches, and rewrite the above 
> example as (note where we use colon vs arrow):
>     int y = switch(abs(x)) {
>         case 1 -> 1;
>         case 2 -> 2;
>         case 3 -> 3;
>         default:
>             println("more than 3");
>             break x;
>     };
> So in this context, then "case L -> e" in an expression switch is just 
> sugar for "case L: break e".  As with lambdas, I expect the 
> statements+break form to be pretty rare, but we still need to have a 
> way to do it (not all objects can be created in a single expression 
> without resorting to stupid tricks.)
> A good way to think about this is that this is leaving statement 
> switch completely alone, and then expression switch "extends" 
> statement switch, adding the nice arrow shorthand and the 
> exhaustiveness analysis.  The downside is that expression switch is 
> even more "infected" by existing switch semantics, but after thinking 
> about it for a while, this doesn't bother me.  (It's more uniform, 
> plus its considerably harder to make the "accidental fallthrough" 
> mistake in an expression switch than a statement switch.)
> I expect this proposal will be a little more controversial than (2) -- 
> mostly because some are probably holding out hope that we'd radically 
> rework existing switch -- but it has the major advantage of further 
> building on existing switch, and also refrains from introducing a 
> similar but different kind of fake block expression.  Overall this is 
> is more of a "build on what's there" solution, rather than "add 
> something new in the gap."

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20171219/5b607c55/attachment.html>

More information about the amber-spec-experts mailing list