dart-lang / dart-lang/language
Switch elements - switches in collections, like `if` and `for`
- Dominant language
- TeX
- Stars
- 2.9k
- Forks
- 239
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 14
Description
The [patterns proposal](https://github.com/dart-lang/language/blob/master/working/0546-patterns/patterns-feature-specification.md#switch-expression) includes an expression switch.
It would likely be very useful to allow it as an "element switch" as well, so that it can be used in collection literals with "element"s as values instead of expression.
(Most likely, it requires duplicating the grammar into the elements grammar as well).
That would allow something like:
```dart
[42, 37, switch (Endian.host) {
case Endian.little: ...[87, 0, 0, 0]
case Endian.big: ...[0, 0, 0, 87]
}, 99]
```
(just picking a random platform enum as example, bigger enums that can't be handled easily by `if` would be a better example).
## Grammar
```ebnf
::=
|
|
|
|
| (* new *)
(* All new: *)
::= 'switch' '(' ')'
'{' * ? '}'
::= ('case' ':')+
::= 'default' ':' ?
```
The grammar is different from switch statements in that:
* The switch element caluses have no syntactic terminator, they are terminated by the following `case`, `default`, `,` or `}`. This is consistent with other elements. _And it precludes using the `case`-free `=>` syntax from expression switch._
* There are no labels. (There are no continues either, so no use for labels.)
* "Empty-bodied" switchs falling through to (combining with, really) the next case is made explicit, by matching them together as `('case' ':')+`
* It *is* an option to not allow multi-case elements.
* Or to allow "no element"-bodies which introduce no element, but that's ambiguous with "fallthrough". For now, use `...[]` for introducing "no value". (Use `?null` when we get null-aware elements.)
* The `default` clause can omit an element clause, which will mean introducing no element. It's there for "must exhust" types".
## Semantics
Matching and binding semantics are the same as for switch statements, the scope is the following element.
As usual, elements guarded by multiple cases with "fallthrough" can only use bindings that have the same type in every pattern binding leading to that element.
A switch element is "must exhaust" in the same cases as a switch statement (switching on enum, bool, Null, sealed type, or combinations of those). It's otherwise not "must exhaust" since it's OK to not introduce any elementrs.
A "must exhaust" switch can use `default:` as a default no-element catch-all.
## Comments.
I wanted to allow `else:` instead of `default:`. It's slightly harder to parse a prior `if`, though. (I'd even have allowed `else element` without a `:`, but that's definitely not possible.)
```dart
var x = [switch (e) {
case p1: if (b2) e3
else: b4
}
```
Not completely ambiguous, but a little confusing. Probably just keep using `default:`, or `case _:` with an element.
If adding this, do add null-aware element at the same time, `? expression` can be written as `switch (expression) { case var v?: v }`, but `...? optList` can be written as `switch (optList) { case var list?: ...list }` too, and we still think it's a good operator.
Contributor guide
Assessment
This issue has not been assessed yet.