break seen as a C archaism

Victor Nazarov asviraspossible at gmail.com
Mon Mar 12 07:13:20 UTC 2018


I'd like to point out one syntax irregularity with current proposal that
nobody has explicitly called out.

Both lambdas and switch expression cases have two forms: single expression
form and multiple statement form.

Besides, most C-inherited sytactic forms are based on the idea that there
is always default single-statement variant which can be converted to
multiple-statement variant, when using block.

The problem with break as I see it is that this story becomes too irregular.

Lambda default is single expression. Block syntax can be used to invoke
multiple-statement variant.
Most control flow syntax default is single-statement. Block syntax can be
used to invoke multiple-statement variant.
With switch expression MULTIPLE-statement is the default. ARROW syntax
should be used to invoke single expression variant. In this context swith
expression story seems to bring significant discrepency...

We can argue that single expression it true default for switch expressions
since it's shortest, but Brian said that it's better to tell switch
expression story from multiple-statement end. I think this indicates that
that multiple statement variant is the most natural variant and language
user should bring some effort to abbreviate expression in single expression
case.

Most problematic for me is that everywhere in Java block syntax (curly
braces) is used to switch (no pun intendent) between single expression /
statement and multiple statement variant. But for switch expressions colon
and arrow is used instead... This seems verry irritating seens whole
language managed to use block syntax everywhere else before...

--
Victor Nazarov

сб, 10 мар. 2018 г., 1:15 Brian Goetz <brian.goetz at oracle.com>:

> 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.
>
>
>
>


More information about the amber-spec-observers mailing list