Good.

This sentence:

  The sugared form of case arms of switch expressions may also throw 
exceptions, even though throw e is a statement, not an expressions. 

(“an expressions”??) could perhaps be replaced or augmented by this 
syntactically more precise observation:

  In a switch expression, we also define

                case LABEL -> throw expression;

  to be sugar for

                case LABEL: throw expression;

Also, there is a formatting problem: the text line

        System.out.println("Neither Foo nor Bar, hmmm..."); break 3; }

should have been part of the code in the preceding box.



> On Dec 19, 2017, at 2:40 PM, Brian Goetz <[email protected]> wrote:
> 
> I've updated the JEP to reflect these proposed changes:
> 
>     https://bugs.openjdk.java.net/browse/JDK-8192963 
> <https://bugs.openjdk.java.net/browse/JDK-8192963>
> 
> 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."  
>> 
>> 
> 

Reply via email to