Expression switch - an alternate proposal

Brian Goetz brian.goetz at oracle.com
Mon Apr 9 14:28:40 UTC 2018



> The goal is to tackle four specific things (in order):
> 1) The context as to whether it is a statement or expression switch
> (and thus what is or is not allowed) is too remote/subtle
> 2) Mixing arrows and colons is confusing to read
> 3) Blocks that do not have a separate scope
> 4) Fall through by default
> while still keeping the design as a unified switch language feature.

It's great you specified your goals clearly.  That automatically makes 
it much more relevant.  (But, it does let me pick on your goals.)

I think (1) and (2) are valid concerns -- they are driven by "what will 
users think when they read this code."  We care about that a lot.

I think (3) is firmly in the "fixing the mistakes of the past" 
category.  If it came for free, then fine, its a nice-to-have -- but 
even then we should be careful.   (I'll share that, in the course of 
working out pattern binding scoping, one of the candidates appeared to 
have the benefit of fixing (3) "for free", which we liked, but clearly 
we gave this side-benefit too much weight, because this aspect kept us 
from seeing an obviously better answer for quite a while.  Lesson: even 
free isn't free.)

On (4), I think you might have been under the mistaken impression that 
we'd already decided on unrestricted fallthrough in expression switches? 
   It has always been on the list to work out the appropriate set of 
restrictions, if any, for expression switches. (Obviously we're not 
doing anything for statement switches.)

> To tackle #1 and #2, all cases in an expression switch must start with
> arrow -> (and all in statement switch must start with colon :)
> To tackle #3, all blocks in an expression switch must have braces
> To tackle #4, disallow fall through in expression switch (or add a
> fallthrough keyword)

Heh.  This was essentially our starting point, with the exception that 
we hadn't yet thought of reusing "break" in expression switches at that 
point.  So I obviously can't criticize it too much...

One serious problem this proposal had the first time around was that 
there wasn't a way to express OR patterns, the expression equivalent of:

     case Foo(var x):
     case Bar(var x):

Even those who hate fallthrough agree that this kind of fallthrough is 
essential, but no one could stand the notation:

     case Foo(var x) ->
     case Bar(var x) -> e;

We later came around to allowing comma-separate patterns as an explicit 
OR mechanism:

     case Foo(var x), Bar(var x) -> ...

which provided a way out of this mess.  It also provided us with the 
_option_ to restrict fallthrough into -> cases, which we didn't have 
before.

> Here is the impact on some code:
>
>     default: -> {

I assume you mean

     default -> { }


> How is this still a unified switch?

By redefining "unified", of course :)

The differences here are, not surprisingly, almost entirely 
syntax-driven, and go all the way down to the parser productions. 
Expression switches have arrows and blocks; statement switches have 
colons and no blocks.  (The other proposed changes, comma-separated 
label lists, alternate target types, and null case handling are the same 
either way.)

The primary non-syntactic difference is that started with a "no 
fallthrough ever in expression switch" requirement, whereas we're still 
deciding on what constraints to put on fallthrough.  But that's not a 
difference, as much as you've already made up your mind and we haven't.

As syntaxes go, there's nothing deeply wrong with it, and I understand 
your motivations (1) and (2) to prefer having a broader syntactic 
difference between the two.  That said, I have a real aversion to 
introducing a block expression syntax here.  It would suck to have a 
block expression syntax that's only good in switches; it would suck in 
different ways to have one that is good everywhere.  So IMO the best way 
to win that game is not to play. (IOW: one of the benefits of sticking 
more literally with an existing construct is it avoids the need to 
invent downstream new constructs like ad-hoc block expressions; one of 
the problem with such ad-hoc features is the inevitable call to expand 
their scope later.)

So, what you've done is move a little bit down "it looks different, so 
people won't be surprised that it acts different" (e.g., different 
exhaustiveness behaviors) spectrum.  (You could go farther down that 
spectrum by having a different keyword.)  Of course, everyone will have 
their own thoughts of how #{ DIFFERENT } the new feature has to look to 
avoid confusing people.

Personally, I think the "people will be confused" issue is just the 
usual Stroustrup's Rule in action.  I think the vast majority of 
expression switches will have all arrows except for maybe the default 
clause, so I don't think in practice any one will actually be confused 
that they're in an expression switch, nor do I think people will be 
tempted to mix the two without knowing what they're doing.  So I think 
these fears are wildly overblown.

The conversation about fallthrough in expressions still has to be had, 
so I won't comment there at this time.

I think we've mined this out pretty well.  I'll move the discussion to 
the EG list.


Cheers,
-Brian






More information about the amber-dev mailing list