Language: jdk 12 like switch statement

Created on 13 Sep 2018  路  9Comments  路  Source: dart-lang/language

As a proposal I'd like to suggest Dart to implement enhancements to the switch statement in the same manner as Java is doing with JDK 12.

The enhancements allow for

  1. More concise code,
  2. Removing the need of a break statement in certain situations
  3. Allowing case statements to return a value.

The new switch features are outlined in good detail here:
https://www.javacodegeeks.com/2018/09/jdk-12-switch-statements-expressions.html

All examples are from article above. I've just added them here from a high level point of view

_Traditional Switch_

String numericString;
switch (integer)
   {
      case 1 :
         numericString = "one";
         break;
      case 2 :
         numericString = "two";
         break;
      case 3:
         numericString = "three";
         break;
      default:
         numericString = "N/A";
   }

_JDK 12 Enhanced Switch_

``` String numericString;
switch (integer)
{
case 1 -> numericString = "one";
case 2 -> numericString = "two";
case 3 -> numericString = "three";
default -> numericString = "N/A";
}

_JDK 12 : New switch Expression Returning Value via break_

```final int integer = 1;
   final String numericString =
      switch (integer)
      {
         case 1 :
            break "uno";
         case 2 :
            break "dos";
         case 3 :
            break "tres";
         default :
            break "N/A";
      };

_JDK 12 : New switch Expression Returning Value via Label Rules_

```final int integer = 4;
final String numericString =
switch (integer)
{
case 1 -> "uno";
case 2 -> "dos";
case 3 -> "tres";
case 4 -> "quatro";
default -> "N/A";
};

_JDK 12 : Multiple Constants Can Be Specified for a Single case_

``` final int integer = 7;
   final String numericString =
      switch (integer)
      {
         case 0 -> "zero";
         case 1, 3, 5, 7, 9 -> "odd";
         case 2, 4, 6, 8, 10 -> "even";
         default -> "N/A";
      };
request

Most helpful comment

What I really think we should do is pattern matching, including full destructuring of objects and collections. That covers all of the use cases of switch and then some.

I'm not currently looking at that as part of the "UI as code" changes because this kind of control flow doesn't seem to be a large component of Flutter UI code.

All 9 comments

"Enhanced Switch" seems to be the same as removing the redundant break, which I agree seems valuable.
The next two seem to boil down to making "switch" into an expression, rather than a statement. In general this might be useful in the "ui as code" paradigm (cc @munificent). Other people have asked for "if" to be an expression, or "while"/"for"/"do".
The last one seems to be possible already, using case 1: case 3: case 5:, though I must admit that as a programmer from the Knuth school, I can certainly get behind Pascal-style 1, 3, 5:. Pascal goes even further by avoiding the case (since each selector only has one statement associated with it, rather than a block as in C-like languages), and allowing ranges for ordinals (e.g. monday .. friday, assuming that these are values from an enum).

What I really think we should do is pattern matching, including full destructuring of objects and collections. That covers all of the use cases of switch and then some.

I'm not currently looking at that as part of the "UI as code" changes because this kind of control flow doesn't seem to be a large component of Flutter UI code.

Pardon the intrusion and raising this from the depths of the netherrealms, but I'd love to note a good use-case for a switch expression and/or pattern matching!

I'm just diving into Flutter and following some tutorials that recommend the BLoC pattern for building apps. Of note, given a Bloc<Event, State>, one might find a function like such:

Stream<State> mapEventToState(Event e) async {
  if (e is Foo) {
    yield SomeState( ... );
  } else if (e is Bar) {
    yield OtherState( ... );
  } else if ....
  } else {
    yield ErrorState();
  }
}

or even in many examples the yet more verbose pattern of

if (e is Foo) {
  yield SomeState();
  return;
}
if (e is Bar) {
  yield OtherState();
  return;
}
...

As written, this is very wordy, especially if your Bloc can handle many types of events! If we look at the state of the art in other languages, constructs like C# 8's switch expression or Rust's match expressions leap out as a fantastic way to clean these up. For example, in C# (and forgive me for making the sample more C#-ey):

Iterable<State> MapEventToState(Event e)
{
    yield return e switch
    {
        Foo _ => new SomeState( ... ),
        Bar _ => new OtherState( ... ),
        ...
        _ => new ErrorState()
    }
}

That is, in my opinion, a major improvement! A lot less noise, more scannable, and much less repetition. Especially handy in this case is the ability write yield (or yield return in C#'s case) only once, saving us from potentially tricky bugs where we introduce a check for a new event type but forget to yield the resulting state.

Based on Kotlin when expression it will be awesome to saw one day switch with arbitrary expressions, in range and instance checks, etc. like:

switch (dynamic x) {
    is! Integer -> print("x is not an integer")
    isSpecialMagicNumber(x) -> print("x is magic number from function")
    in 1..10 -> print("x is in the range")
    in validNumbers -> print("x is in array")
    !in 10..20 -> print("x is outside the range")
    else -> print("none of the above")
}

@thefuzzyorange, maybe you could re-target this issue to be about improvements of the Dart switch statement in a broader sense (that is, change the title to something like 'Improve the switch statement')?

Then we could gather several issues about switch here, and maintain a good overview of the proposals: #307, #703, #745.

... (Deleted several incoherent posts and replaced them with a separate proposal #762. The new proposal treats switch-expression as a variant of a more general concept).

Scala one is better

Is there a status on this issue? Can we revive it?

I think @sosite made an excellent point; Kotlin's when essentially encapsulates everything you could want:

  • switch (when) as expression
  • Uses == by default, but allows for insertion of custom logic
  • Allows for multiple cases to be bundled (Pascal style)

There's also another issue floating somewhere about not having to retype the first section of an enum in every case statement. I'll link it if I find it (EDIT: #357), but that also seems more relevant when we can bundle cases.

Heck, even JDK 14 switch is getting pretty close to ideal.

Also: @munificent I think this could easily be part of the UI as code paradigm.

var myApp = MaterialApp (
    // some other stuff here
    home: switch (getBreakpoint()) { //where getBreakpoint is a enum of type BreakPoint
         case .xL -> xLHomePage();
         case .L -> LHomePage();
         case .mL -> mLHomePage();
         case .m -> mHomePage();
         case .sM -> sMHomePage();
         case .s -> sHomePage();
    }
)

This a small and somewhat contrived example, but it serves the purpose. If you have to switch between 3+ widgets based on a certain value, this is pretty time-saving.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

moneer-muntazah picture moneer-muntazah  路  3Comments

munificent picture munificent  路  5Comments

marcelgarus picture marcelgarus  路  3Comments

ShivamArora picture ShivamArora  路  3Comments

stategen picture stategen  路  4Comments