language icon indicating copy to clipboard operation
language copied to clipboard

Allow `default` in a switch expression (as an alias for `_`)

Open Levi-Lesches opened this issue 1 year ago • 16 comments

Using _ => defaultValue is a pretty common pattern in a switch expression. But default is more natural for, well, default values. It would be more readable if switch expressions allowed you to use default to denote that this is the "catch-all" case.

String getName(int index) => switch(index) {
  1 => "One",
  2 => "Two",
  3 => "Three",
  default => "Some number",
  // instead of 
  _ => "Some number",
};

Levi-Lesches avatar Aug 30 '23 15:08 Levi-Lesches

I could be wrong, but I don't think this is worth doing. Using _ is quite a bit shorter than default. Honestly, I wouldn't be surprised if _ became the idiomatic way to write default cases in switch statements too.

munificent avatar Aug 30 '23 18:08 munificent

It's not the character length, it's the readability. I know _ is often used to symbolize "we don't care about the value" but there's a slight difference between "value we weren't going to use" and "value that didn't meet our condition". Also, if the meaning of _ is "default value", it makes sense to be able to say default => value.

EDIT: also, if default is used more often (as in the enhanced default parameters/arguments proposals that would enable copyWith), this would make its usage more uniform

Levi-Lesches avatar Aug 30 '23 18:08 Levi-Lesches

This is closed way too fast IMO. Folks don't even have the time to vote

I very much prefer writing default over _ too. Overall switch expressions are too different from switch statements to me. I would be in favour of anything that bridges the gap between the two syntaxes.

This includes default, among other things.

rrousselGit avatar Aug 30 '23 18:08 rrousselGit

I would add:

default is a reserved keyword. If we can't even use it inside a switch when that it's only usage and should use _ instead, then let's remove default from the list of reserved keywords.

"default" is an important english word. Not being able to name a variable default is quite problematic

rrousselGit avatar Aug 30 '23 18:08 rrousselGit

Overall switch expressions are too different from switch statements to me. I would be in favour of anything that bridges the gap between the two syntaxes.

default is a reserved keyword. If we can't even use it inside a switch when that it's only usage and should use _ instead, then let's remove default from the list of reserved keywords.

We could agree on removing default and using only _. There were many times that I wanted to name something as default and couldn't.

This way we would be both consistent and more flexible.

mateusfccp avatar Aug 30 '23 18:08 mateusfccp

I still think default is much more readable. And it's more intuitive to newcomers too.

default is used in many languages, but _ is quite rare overall.

rrousselGit avatar Aug 30 '23 19:08 rrousselGit

Or, use the approach Dart usually takes -- make default a contextual keyword, which only becomes reserved in specific contexts, like switch expressions. Quite a few Dart keywords are actually only contextual, like async, show, and yield (subscripts 1 and 3 in the table).

Levi-Lesches avatar Aug 30 '23 19:08 Levi-Lesches

The default case inside a switch means 'Execute the corresponding body when none of the cases match'. And the wildcard pattern _ inside a match expression means 'Execute the corresponding body when none of the patterns specified above it match'. They have different semantics(one can and should be allowed to write default case at top but _ always comes last). Patterns inside a match expression cannot be re-arranged as freely as cases inside a switch because the way they execute is and should be different and this is enough to make them two different constructs. Overloading the keyword switch to mean match expressions is the exact reason why people are getting confused and overloading default/_ will make things even more confusing.

I agree that _ is not natural at first. I don't know the exact reason why many languages decided to use _ instead of something random to represent a wildcard pattern but it's very common. It's short, easy to type and read, and you'll get used to it fast. I've seen people going further to define unique distinct semantics for this anonymous variable(_):

  • If users are allowed to bind anonymous variable then it gets bound during matching:

    var res = switch (a) {
        1 => "One",
        2 => "Two",
        _ => "Do something with $_",
        // It's better than:
        // _ => "Do something with $a",// to write a, I've to first do a look up at switch(_)
    };
    
  • Or anonymous variable are always unbound:

    // which allows this
    void f(_, _, _) {}
    
    // better than this
    void f(_, __, ___) {}
    
  • Having them bound at some locations and not at others, results in:

    String a = '1';
    var    _ = '2';
    
    var res = switch (1) {
        String a => a.toUpperCase(),
           var _ => _,
    };
    
    print(res); // see if you can guess the value of 'res' given that switch('hi') gives you 'HI'
    

Anonymous variable(_) carries no description of value so having it always unbound makes more sense to me.

hamsbrar avatar Sep 09 '23 02:09 hamsbrar

And the wildcard pattern _ inside a match expression means 'Execute the corresponding body when none of the patterns specified above it match'.

I think your definition, "when none of the patterns specified above it match", is a bit flawed. I interpret _ as a pattern. This pattern match anything. So, the question you should be asking when using a switch statement is, 'Does the variable x match against the pattern y'? Which in _ case means 'Does the variable x match against the pattern _, a pattern that match all possible values".

I agree that _ is not natural at first. I don't know the exact reason why many languages decided to use _ instead of something random to represent a wildcard pattern but it's very common.

Given the prior definition I read it naturally. The answer is yes it matches.

I do agree that default and _ have different semantics thought.

rubenferreira97 avatar Sep 09 '23 10:09 rubenferreira97

I think your definition, "when none of the patterns specified above it match", is a bit flawed.

v v v v   d   w
a a a a   o p h E
r r r r     r i N
            i l V
l g t m     n e .
= = = =     t   y
' ' ' '         o
l g t m     l   u
o o o e     +   A
o o '       g   r
k d         +   e
s '         t   N
'           +   o
            m   t
                S
                u
                r
                e

hamsbrar avatar Sep 09 '23 12:09 hamsbrar

I interpret _ as a pattern... Given the prior definition I read it naturally.

I understand what you mean by that part. I can start seeing _ as having a special meaning just like I see special meaning in ! and ||(logical or). It won't be natural at first and neither was || but overtime I'll get used to it and it'll become almost natural. I'm not sure if that's what you were trying to say but I can't think of any other logical explanation of you interpreting _ as a pattern.

hamsbrar avatar Sep 09 '23 15:09 hamsbrar

I was also hoping to use default in switch expressions. With syntax highlighting, _ is easy to overlook, whereas keywords are typically colored differently. I also find default to be more pleasing to look at, but I'm not sure if others share that opinion.

An exaggerated example:


final withoutDefault = switch ((2, 5, 4, 3)) {
  (1, int a, 3, _) => 1,
  (_, 3, _, 5, 5) => 2,
  (double a, 6, _, 3) => 3,
  (_, 1, _, 3) => 4,
  (_, 4, _, _) => 5,
  _ => 6, 
};


final withDefault = switch ((2, 5, 4, 3)) {
  (1, int a, 3, _) => 1,
  (_, 3, _, 5, 5) => 2,
  (double a, 6, _, 3) => 3,
  (_, 1, _, 3) => 4,
  (_, 4, _, _) => 5,
  default => 6, 
};

It's not a huge change -- and not something to delay other language features for, in my opinion -- but it would be nice to have.

davidhicks980 avatar Sep 13 '23 09:09 davidhicks980

it would be good to have default be a valid way to express the empty pattern, since when I switched to using switch expressions, I expected them to be syntactically similar to normal switch, like in Java or even kotlin.

WarpspeedSCP avatar Sep 21 '23 13:09 WarpspeedSCP

They have different semantics(one can and should be allowed to write default case at top but _ always comes last).

There is no value in having a default case appear before any other cases because it would mean those later cases are definitely never reachable and thus dead code.

The _ pattern is useful in multiple places because it can be nested inside larger patterns. Even as a standalone pattern in a case, it can be useful outside of the last case because of guards:

switch (numbers) {
  case []: print('Empty');
  case [1]: print('Contains only 1');
  case _ when numbers.contains(1): print('Contains 1 somewhere in it.');
  case _: print('Does not contain 1.');
}

Default cases are less useful because they don't have guards. Really, the only thing default has going for it is age and familiarity. It's more verbose, less flexible, and less expressive.

munificent avatar Jan 18 '24 02:01 munificent

If default is just an alias for case _ then isn't it just as (in)expressive/flexible? Also, it's 1 character longer but I wouldn't call it more verbose because it's just one word that does exactly what it says.

Levi-Lesches avatar Jan 18 '24 18:01 Levi-Lesches

If default is just an alias for case _ then isn't it just as (in)expressive/flexible?

It's not simply an alias for case _, because case _ can have a guard (when clause) but default can't. It's less flexible than _ because default can only appear as a standalone case, but _ can be nested in other patterns.

munificent avatar Jan 19 '24 02:01 munificent