Expression switch - an alternate proposal

Cay Horstmann cay.horstmann at sjsu.edu
Thu Apr 12 22:57:20 UTC 2018


I am in agreement that

       topping = switch (food) {
           case BURGER -> KETCHUP;
           case SAUSAGE, ASPARAGUS -> MUSTARD;
           case ICE_CREAM -> FUDGE;
           default -> NOTHING;
       }

is good, and that I would be happy to explain it before statement 
switch. (Mustard on asparagus, though?)

But the switch from

         case BAKED_POTATO -> SOUR_CREAM

to

         case BAKED_POTATO:
               Toppings t = new Toppings();
               t.add(BACON);
               t.add(SOUR_CREAM);
               t.add(CHEESE);
               t.add(BACON);  // not a typo
               break t;

is a problem.

It is quite different from the equivalent transformation with lambda 
expressions:

       Callable<TOPPING> task = () -> SOUR_CREAM;

to

       Callable<TOPPING> task = () -> {
               Toppings t = new Toppings();
               t.add(BACON);
               t.add(SOUR_CREAM);
               t.add(CHEESE);
               t.add(BACON);  // not a typo
               return t;
          };

And it requires a new construct, break with value, that has no use 
anywhere else (one hopes).

Perhaps you shouldn't allow statements in a switch expression? They 
aren't allowed in a ? : expression either.

I also don't understand Rémy's remark that a traditional break inside a 
switch expression is disallowed. What rules out

         case BAKED_POTATO:
               Toppings t = new Toppings();
               t.add(SOUR_CREAM);
               t.add(CHEESE);
               int count = 0;
               while (true) {
                  if (count == 10) break; else count++;
                  t.add(BACON);
               }
               break t;

Or more horrifyingly,

         case BAKED_POTATO:
               Toppings t = new Toppings();
               t.add(SOUR_CREAM);
               t.add(CHEESE);
               int count = 0;
               while (true) {
                  if (count == 10) break t; else count++;
                  t.add(BACON);
               }

Is there a restriction on what can go into the semicolon-separated list 
of statements in the case label of an expression switch? I couldn't see 
one in the JEP. And if not, what happens if there is a return? A break 
without value? A continue?

What about variable declarations? In statement switch, the following is 
legal:

switch (foo) {
    case 1:
       int x = 1;
    default:
       x = 2;
       System.out.println(x);
}

and the following is not:

switch (foo) {
    case 1:
       int x = 1;
       System.out.println(x);
       break;
    case 2:
       int x = 2;
       System.out.println(x);
       break;
}

It seems that, if variable declarations are allowed in an expression 
switch, the rules are going to have to be different :-)

If you shore this up by restricting the statements that can follow a 
case label in an expression switch, haven't you implicitly defined a 
block expression? In that case, why not make it a general construct?

Cheers,

Cay


Le 12/04/2018 à 05:48, Brian Goetz a écrit :
> I think Remi has it exactly right here: this is a pedagogical blessing 
> in disguise -- as long as we can let go our of pre-existing notions of 
> how it ought to be taught.
> 
> The switch statement is nasty and complicated in multiple ways; surely 
> you don't want to teach that first if you have a choice. So start with 
> the simplified form of the switch expression:
> 
>      topping = switch (food) {
>          case BURGER -> KETCHUP;
>          case SAUSAGE, ASPARAGUS -> MUSTARD;
>          case ICE_CREAM -> FUDGE;
>          default -> NOTHING;
>      }
> 
> Students should be able to grasp this immediately; if its one of the 
> things on the left, pick the thing on the right.  You can get a long way 
> with this.
> 
> Eventually you will come across a situation where the right-hand side is 
> not a simple expression.  Then you can teach them the escape hatch for 
> adding statements into the mix:
> 
>      topping = switch (food) {
>          case BURGER -> KETCHUP;
>          case SAUSAGE, ASPARAGUS -> MUSTARD;
>          case ICE_CREAM -> FUDGE;
>          case BAKED_POTATO:
>              Toppings t = new Toppings();
>              t.add(BACON);
>              t.add(SOUR_CREAM);
>              t.add(CHEESE);
>              t.add(BACON);  // not a typo
>              break t;
>          default -> NOTHING;
>      }
> 
> You can explain that "break" in a switch is like "return" in a method. 
> While the "spelling" might seem weird, the concept is not hard.
> 
> Now, when you need to, you can teach the switch statement in all its 
> glory.  They've already seen switching; they've already seen breaking; 
> what's left it to explain fallthrough (no way around this) and the lack 
> of exhaustiveness.
> 
> 
> Which is to say:
> 
>   - New users can probably learn the concepts better by adding them in 
> one at a time; first simple expression switches, then adding in break, 
> then statement switches.
> 
>   - Existing users, who have already been through the gauntlet of 
> learning fallthrough, will necessarily learn it in the opposite order, 
> but they should be able to recognize that break means the same thing it 
> always has, so there are still relatively few new concepts here.
> 
> This seems like a pretty good pedagogical story either way!
> 
> 
> 
> On 4/12/2018 3:23 AM, Remi Forax wrote:
>> ----- Mail original -----
>>> De: "Cay Horstmann" <cay.horstmann at sjsu.edu>
>>> À: "amber-dev" <amber-dev at openjdk.java.net>
>>> Envoyé: Jeudi 12 Avril 2018 06:50:35
>>> Objet: Re: Expression switch - an alternate proposal
>>> I've thought about this more and in the end decided to speak up. Over
>>> the years, I explained a substantial number of Java features to a very
>>> large number of people, and I generally have a pretty good idea of where
>>> people struggle. Anonymous inner classes, wildcards, constructor
>>> references, the unnamed module, you name it. With all of these features,
>>> I knew I had my work cut out explaining them, but I wasn't too bothered.
>>>
>>> I am bothered by the proposed expression switch syntax.
>>>
>>> When I read these discussions about colons vs. arrows, I fear that they
>>> mask a much bigger issue. I do not think that there is significant
>>> transfer from the existing switch statement to what we want to achieve
>>> with expression switch. The matching part is similar, but the control
>>> flow is not.
>> the control flow of an expression switch is a subset of the control 
>> flow of a statement switch because it's an expression.
>>
>>> Maybe I am wrong. If there has been user testing that confirms that
>>> programmers properly transfer their knowledge of statement switch to
>>> expression switch with the "break x" syntax, that's great, and I'll shut
>>> up. In absence of such evidence, I would urge (1) to make expression
>>> switch visibly different from statement switch and (2) have an ironclad
>>> justification for "break x" or abandon it. It has tension with labeled
>>> break and classic break which can be present inside a branch of an
>>> expression switch. Teaching a break/return analogy does not seem any
>>> easier than teaching about block expressions.
>> You can not have a break label (or a continue) inside a switch 
>> expression, again it's an expression, it's like ?:, you can have a 
>> break label into it.
>>
>> I disagree that teaching block expressions is as easy that teaching 
>> break expression, with block expressions, you also have to teach why 
>> you can not use it anywhere else in Java.
>> The break expression does not have this issue. BTW, i think it's easy 
>> in term of teaching to teach the switch expression first and then to 
>> teach the statement switch given that the expression switch semantics 
>> is a subset of the statement switch semantics.
>>
>>> Cheers,
>>>
>>> Cay
>>>
>>>
>> Rémi
>>
>>>
>>> Le 10/04/2018 à 13:59, Stephen Colebourne a écrit :
>>>> On 10 April 2018 at 09:02,  <forax at univ-mlv.fr> wrote:
>>>>>>> Basically, your proposal is to use -> eveywhere, i think i prefer 
>>>>>>> the opposite,
>>>>>>> do not use arrow at all.
>>>> Current Oracle proposal:
>>>> statements = colon
>>>> expression = colon, with arrow for expressions
>>>>
>>>> Alternate proposal:
>>>> statements = colon
>>>> expression = symbol-that-isnt-colon
>>>>
>>>> Remi proposal:
>>>> statements = colon
>>>> expression = colon
>>>>
>>>> If the goal is a unified switch expression, Remi proposal wins. It is
>>>> dead simple and very consistent.
>>>>
>>>>>> This is a reasonable alternative, but I don't think it would be very
>>>>>> popular.  I think people will really love being able to write:
>>>>>>
>>>>>>       case MONDAY -> 1;
>>>>>>       case TUESDAY -> 2;
>>>>>>
>>>>>> and will be sad if we make them write
>>>>>>
>>>>>>       case MONDAY: break 1;
>>>>>>       case TUESDAY: break 2;
>>>> Meh. Three more characters.
>>>>
>>>> Yet:
>>>> - Avoids the arrow having a conflict of meaning with lambda.
>>>> - No mixed arrows & colons
>>>> - Much more consistent.
>>>> - Minimal change from existing switch.
>>>> - Less to learn.
>>>> Win, win, win.
>>>>
>>>> So while I'd still choose to have a separate symbol for expression and
>>>> statement switches (because of my #1 goal), I'm also pretty fine with
>>>> the Remi proposal (because my #2 goal).
>>>>
>>>> In fact, what the discussion has informed me is that my #1 and #2
>>>> goals are the wrong way around. Getting rid of the mixed arrows and
>>>> colons is now more important to me than understanding the context in a
>>>> large switch.
>>>>
>>>> Stephen
>>>>
>>>
>>> -- 
>>>
>>> Cay S. Horstmann | http://horstmann.com | mailto:cay at horstmann.com
> 


-- 

Cay S. Horstmann | http://horstmann.com | mailto:cay at horstmann.com


More information about the amber-dev mailing list