New JEP: Switch Expressions for the Java Language
Brian Goetz
brian.goetz at oracle.com
Mon Dec 11 19:59:27 UTC 2017
On 12/11/2017 7:04 AM, Stephen Colebourne wrote:
> At first, my reaction was "great!", but after doing some analysis I'm
> left feeling this isn't going to work well as proposed...
>
> To summarise the proposal, expression switches get:
> - comma separated OR values
> - case null
> - no fall through
> - enforced default
> - auto default clause for enums
>
> And statement switches get none of these (AFAICT).
Incorrect on the first two items. JEP says:
repeated labels: "A case clause *in a switch statement or expression
*can provide a comma-separated list of alternatives, such as:"
null: "In *a switch expression **or statement *whose whose argument is a
reference type (boxed primitives, strings, and enums), a case clause can
can specify null:"
On the other three, these are driven by either intrinsic properties of
expression-ness, or compatibility:
- no fall through -- well, fall through doesn't make sense (in its
unvarnished form) in expression switch.
- enforced default -- expressions must be total; conditional
statements can be unbalanced (like if with no else)
- auto default clause for enums -- can't do that to statement switch,
it would change the semantics of existing code.
> But if expression switches have no requirement to use the return value
> (as per the rest of Java), then an expression switch can be used in
> all circumstances that a statement switch is used today. And since the
> expression switch would essentially be better than the statement
> switch in every way, I believe best practice would effectively
> deprecate statement switches in most cases.
I think this is mostly a silly argument; just as writing a switch
statement today which really wants to be an expression is annoying
(declare the variable up top, assign in each arm), doing the reverse
with expression switch (ignoring the return value, using the block form
for statements) would be equally annoying.
The one reason people might do so is to get away from "break"; the
others are pretty cornery. Now I get that people hate the choice of
fall-through as the default in statement switch, and hate-filled people
will frequently do self-destructive and stupid things. But do you
really think people will prefer to write:
switch (foo.color) {
case Color.RED -> {
System.out.println("It's a red foo!");
return null;
}
case Color.BLUE -> {
System.out.println("It's a blue foo!");
return null;
}
default -> null;
}
over:
switch (foo.color) {
case Color.RED:
System.out.println("It's a red foo!"); break;
case Color.BLUE:
System.out.println("It's a blue foo!"); break;
}
Seems like frying-pan-to-fire territory to me. And, even if they did,
could you imagine the industry actually blessing that a "best practice"
(note: that's a trick question, since "best practice" is a meaningless
term.) But this is not something good programmers would do. If you
want to operate by side-effects, use statements; if you want to produce
a value, use expressions. I think most developers understand this.
> I examined the OpenGamma private codebase to see how we use switch.
> The vast majority of uses are from Joda-Beans as follows:
... which is to say, most uses of statement switch are simulating
expression switch. Which is why the lack of expression switch seems to
be a bug -- most of the time, people are simulating the mechanism they
want with the mechanism they have.
> - Occasional example where two or three variables were being assigned
> by the switch (something that expression switch could not handle
> without using a messy Pair/Triple).
Did you try rewriting these as expression switches? Did you like the
result? I think you won't, because ... they're not expressions.
> Having looked at our examples though, a lot of the common 80%+ group
> involve a block of code for each case, not a single expression. These
> would be converted to expression switches as follows;
>
> private String someMethod(String foo) {
> return switch (foo) {
> case "a", "b" -> {
> // do something
> return "AAA";
> }
> case "c", "d" -> {
> // do something else
> return "ZZZ";
> }
> default -> {
> throw new IllegalArgumentException();
> }
> }
> }
Note that these can often be profitably split into two separate ops:
String s = switch(foo) {
case "a", "b" -> "AAA";
case "c", "d" -> "BBB";
case "e", "F" -> "CCC";
...
}
switch (s) {
case "AAA": something()
// nothing to do for BBB
case "CCC": somethingElse();
}
> To summarise - having looked at our uses of switch, I think that
> adding expression switch as proposed (without enhancements to
> statement switch) might well cause migration to expression switch that
> makes code read worse
So far, you've not presented any arguments for why people would do so,
and all the examples I can imagine are much uglier -- which provides a
natural deterrent. Where is this assumption coming from?
> ** Given this situation, I think it is essential for the statement
> switch to be enhanced alongside the addition of expression switch. **
Which the JEP proposes on the first two counts.
If you search your heart, you'll realize that the break / fallthrough
thing is just an emotional "I think this was a mistake and I want an
excuse to fix it." We didn't drop fallthrough from expression switch
because we hate it (though we might), but because it simply *doesn't
make sense* in an expression context. But it does in a statement
context -- regardless of how much you don't like it.
A throwing auto-default is a non-starter in a statement context. Not
only would it break tons of perfectly valid code, but in many cases it
would be annoying to have to provide a do-nothing default clause just to
defeat this. I can't imagine you're suggesting that. A non-throwing
auto-default, well, that's what you've got in today's world already...
> Given this, it would seem to me that perhaps expression switch
> should not have blocks. We pretty much never use them with lambdas.
Deeply incorrect. Not only do they get used, but if we didn't have
them, the howls would be deafening, as people would have to do all sort
of distortions to their code to work around this. The situation with
statement lambdas is ideal -- they are legal, safe, and rare.
More information about the amber-dev
mailing list