break seen as a C archaism
forax at univ-mlv.fr
forax at univ-mlv.fr
Fri Mar 9 23:24:06 UTC 2018
----- Mail original -----
> De: "Brian Goetz" <brian.goetz at oracle.com>
> À: "Remi Forax" <forax at univ-mlv.fr>, "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
> Envoyé: Vendredi 9 Mars 2018 23:15:21
> Objet: Re: break seen as a C archaism
> I understand where these people are coming from. But my experience is,
> with a platform as mature as Java, one must be very, very careful of the
> desire to opportunistically "fix" "mistakes" of the past, it can be a
> siren song that draws you to the rocks. I am skeptical of arguments
> that "we should kill break (or at least, not make it more important),
> because it's old stuff and we're young and hip", even though I have a
> certain sympathy for this argument. (Well, I'm old, and was never hip,
> but I'd like to try it someday.)
>
> Fallthrough is certainly one of the biggest punching bags of Java.
> However, the problem with fallthrough is not fallthrough itself, but the
> fact that fallthrough is the default, when 98% of the time you do not
> want fallthrough. That's a separate problem -- and might admit a
> separate solution.
>
> In switch expressions, 98% of the time, except maybe in the default arm,
> no one will ever have to type break, because you can usually say:
>
> int x = switch (y) {
> case 1 -> 2;
> case 2 -> 4;
> case 3 -> 8;
> default:
> throw new TooBigException();
> }
>
> See, no break. But sometimes, you will need it.
>
> There are basically two stable ways we can go here:
> - Renovate switch as best we can to support expressions and patterns.
> - Leave switch in the legacy bin, and make a new construct, say
> "match". (Note that doing this helps with the fallthrough-by-default,
> but doesn't really help switch expressions at all -- we still have to
> solve the same problem.)
>
> There are costs to both, of course. (Engineers tend to over-rotate
> towards the second because it seems more fun and modern, but sticking
> with what works, and what Java developers _already understand_, is often
> better.) Our current strategy is to stick with what works until that
> approach is proven unworkable.
>
> I think trying to "tame" switch is less stable; if we're going to stick
> with switch, we should avoid unnecessary discontinuities between make
> statement and expression switch.
>
>> For others, it elevates the status of break and break is seen as something
>> wrong, an archaism from C.
>
> I think this is really another form of the emotional "its ugly" reaction.
>
>> When i asked what we should do instead, the answer is either:
>> 1/ we should not allow block of codes in the expression switch but only
>> expression
>
> This option is not only dislikable (as you suggest), but naive. While
> most of the time, you can say what you want in one expression, there
> will be times where you'll want to do things like the following:
>
> String y = switch (x) {
> case 1:
> System.out.println("DEBUG: found where the 1 is coming from!");
> break "one";
>
> case 2:
> if (throwOnTwo)
> throw new TwoException();
> else
> break "two";
>
> case 3:
> StringMaker sm = new StringMaker();
> sm.setSeed(System.currentTimeMillis());
> break sm.randomString();
> }
>
> While all of these are likely to be infrequent, telling people "just
> refactor your switch to a chain of if-then-else if you want to do that"
> is going to go over like the proverbial lead balloon.
>
> So, no to that one.
>
>> 2/ that we should use the lambda syntax with return, even if the semantics is
>> different from the lambda semantics.
>
> Yes, we considered this, and actually thought this was a clever idea for
> a short while. (General lesson: beware of clever-seeming ideas.) But,
> among other problems, reusing "return" in this manner is even more of an
> abuse than reusing "break". It's a credible choice, but it has its own
> problems too.
>
>> So should we backup and re-use the lambda semantics instead of enhancing break ?
>
> Pesonally, I think if we're going to stick with switch, the current
> proposal -- which generalizes the existing switch semantics -- is
> staying more true to what switch is. If we find we have to abandon
> switch and do something else, then I think many more options are on the
> table.
>
> BTW, I think most people misunderstand the current proposal because its
> usually explained backwards. So let me explain it forwards, and I think
> it makes more sense this way.
>
> STEP 1: Extend switch so it can be an expression. Extend break so it
> can take a value to be yielded from the switch expression.
>
> This means that everything about statement switches and expression
> switches are the same, except that a statement switch can terminate
> nonlocally and an expression switch can't. But it is a very
> straightforward extension of the control flow of switch to expressions,
> and that's a plus. What's ugly about it is that you have to say break a
> lot:
>
> int x = switch (y) {
> case 1: break 2;
> case 2: break 4;
> case 3: break 8;
> default: throw new TooBigException();
> }
>
> Which brings us to step two, which is purely a syntactic optimization
> for the very common case where an expression switch arm has no
> statements other than break:
>
> STEP 2: In a switch expression, allow:
>
> case label -> e
>
> as shorthand for
>
> case label: break e
>
> (much as an expression lambda is a shorthand for a statement lambda.)
>
> If you explain it the other way, people think that -> is what makes the
> switch an expression switch, and then the break rule seems weirder.
I think part of the confusion comes from the fact that we reuse '->' which is strongly associated to lambdas,
perhaps a way to avoid that is to not reuse the arrow.
When you say expression lambda is a shorthand for statement lambda, nevertheless, both form use the arrow,
again, what if we do not use an arrow for the shorthand case ?
Technically, i do not think we need a symbol at all, but it will be ugly because we also want to have multiple values for the case separated by comma.
So let say we need a symbol but not '->', perhaps ':>' may work ?
int x = switch (y) {
case 1 :> 2;
case 2 :> 4;
case 3 :> 8;
default:
throw new TooBigException();
};
Rémi
More information about the amber-spec-experts
mailing list