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 <[email protected]
<mailto:[email protected]>> 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."