Switch expressions -- some revisions
Brian Goetz
brian.goetz at oracle.com
Tue Dec 19 20:21:41 UTC 2017
Updated.
On 12/19/2017 3:03 PM, Guy Steele wrote:
> 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 <brian.goetz at oracle.com
>> <mailto:brian.goetz at oracle.com>> wrote:
>>
>> I've updated the JEP to reflect these proposed changes:
>>
>> 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."
>>>
>>>
>>
>
More information about the amber-spec-observers
mailing list