-
Notifications
You must be signed in to change notification settings - Fork 207
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Switch expression doesn't actually seem to be an expression. #3061
Comments
But according to this section of the specification:
|
For the future readers: According to the spec
So this code compiles: enum Color { red, blue, green }
void main() {
final color = Color.red;
(switch (color) {
Color.red => "red",
Color.blue => "blue",
Color.green => "green",
});
} |
I'll add all this to the site docs, thank you both |
Unfortunate that a different keyword, like match, wasn't utilized for the expression form. This parenthesized form is not ideal. |
@gamebox The essential problem here is, as written in the spec,
This implies improving the parser would fix this issue without redesigning the language itself (e.g. introducing a new keyword for the expression form). This may also mean improving the parser in such a way would make compilation speed slower. Anyway, I hope this limitation will be removed in the future. |
Yes, that's why I'm saying that having different keywords for the statement versus the expression (which has different syntax anyway), this ambiguity goes away A different onset like |
Can we re-open this? I get that this is in spec, but lets repurpose this issue as a proposal to change the spec? Or I can file a new issue if that's preferrable. The way I assumed the new switch syntax was going to work was that this is simply the new syntax, and while the old syntax may stick around for backwards compatibility, it is overly verbose/awkward and will rapidly be considered an anti-pattern. But instead I now either have to live with two inconsistent syntaxes in my code base or I have to do the weird paren hack. Both of these seem pretty undesirable, especially given how undiscoverable the paren hack is. Also, this line from the spec just seems incorrect to me:
This is not a rare case. Anywhere where the old syntax is used I (and I think most devs) want to use the new syntax instead for consistency and because it's just flat out more writeable and readable.
final someString = switch (someInt) {
// All the lines execute and the case evaluates to the last expression.
1 {
print(1);
someSideEffect();
"foo";
},
2 => "bar",
_ => "baz",
} This just ports syntax conventions even further from functions where I don't think it's just me that finds the bifurcated syntax confusing, see engagement on a related tweet: I know very little about compilation and parsing so I don't know the best way to implement a fix here-I'm just describing what I think the ideal customer-facing behavior is. |
Switch statements are not old syntax that is going away. They are the idiomatic way to do multi-way branching in a statement position. A switch statement is what you need if any case body needs to be a statement and not an expression. Also, switch statements can contain multiple statements in a case body, and they can have multiple cases share a body. Switch statements are great. If you are writing a statement, and you want it to switch, a switch statement is the idiomatic way to do it. That was true before Dart 3.0 and is true today. We didn't deprecate switch statements. We made switch statements much more powerful by allowing them to contain patterns in their cases. We also added switch expressions, because you can't use a statement where an expression is needed. Just as most Dart users wouldn't write: main() {
someCondition ?
print('True');
:
print('False');
} And would instead use the statement form: main() {
if (someCondition) {
print('True');
} else {
print('False');
}
} Instead of: void main() {
final color = Color.red;
(switch (color) {
Color.red => print("red"),
Color.blue => print("blue"),
Color.green => print("green"),
});
} The idiomatic thing to write today is the same as it was before Dart 3: void main() {
final color = Color.red;
switch (color) {
case Color.red: print("red");
case Color.blue: print("blue");
case Color.green: print("green");
}
} Note that the original example is a good example for why a switch expression isn't what you want: void main() {
final color = Color.red;
(switch (color) {
Color.red => "red",
Color.blue => "blue",
Color.green => "green",
});
} This code compiles, but it doesn't do anything. You have a switch expression that does nothing but produce a value which is immediately discarded because the switch expression is in a statement position. If this had been written as a switch statement, it becomes more obvious that those case bodies are pointless. |
FYI we did try to adjust the site docs based on this issue. Albeit as brief and "language tour"-like as possible:
|
I get that this is the intent of the Dart team-I'm proposing that, at the very least, Dart shouldn't arbitrarily limit where developers can use the expression syntax (I'll quit saying "old" and "new" because that was unnecessarily charged language on my part). Here is my reasoning for allowing developers to use the expression syntax at the start of statements (please forgive me if I use some of the wrong terms here-I'm approaching this as a developer using the language, not as a language designer):
To respond to your points directly:
I'd posit that ternaries as statements are non-idiomatic because ternaries have poor readability and are too opinionated (require exhaustivity, don't support multi-statement bodies, don't support more than 2 cases, confusingly flip the If Dart wants there to be a sharp switch expression/switch statement demarcation like there is with ternaries and
Is this really a value add that needs to be forced by a special-case limitation (switch expression can't be used at the start of a statement expression) to the language? AFAIK Dart isn't generally opinionated against using expressions as statements, and I don't see any first-principles specific to switches that suggest it should be opinionated about it here.
I wish switch expressions could have multi statement bodies. Maybe my Scala experience is showing too much here, but its philosophy of "every code block is an expression" is REALLY nice to work with and something that I think oughta be ported to other languages wherever it makes sense. As in my previous post, I think switch expressions should allow multi statement cases using
Pattern matching trivially supports multiple cases with the same body. In fact, I find it much more intuitive and readable than the fall-through based approach that switch statements use because it just re-uses the var foo = switch (someInt) {
(1 || 2) => 'one or two',
(_) => 'some other number',
}; Expression statements don't have an equivalent to labels, but that seems like a pretty minor loss. Labels are such a weird special-case feature that (IMO) would be just as well if not better served by a helper function/method. TLDR: I think there are a lot of compelling reasons to allow a switch expression to be used as a statement. If Dart were being remade from scratch today, I think it'd be hard to argue that there should be two distinct switch syntaxes, one for expressions and one for statements. As such, while it may be unreasonable to deprecate the statement syntax (I'd challenge even this claim, though that's probably best left for another time), at the very least lets not place artificial limits on when and where the expression syntax can be used. |
Can we re-open this bug or should I file a new issue and port the conversation here? |
I've reopened this to discuss on the team. |
@caseycrogers, I find all of your points pretty compelling. I think the existing switch statement syntax is familiar and easier to learn for users coming from some other C-derived language, but I agree that if you aren't steeped in that tradition, it sticks out like a sore thumb. (I could insert an essay here about how the design of switch statements in C is consistent with the rest of the language when you consider a point in time when labels and
I have a pretty strong preference for everything-is-an-expression languages too. But the initial designers of Dart did not and it's not clear to me if there is a path to get there that doesn't leave a lot of confusion and mess in its wake. I'm interested in exploring this, but I suspect it will be a bridge too far.
Filing a separate issue (if you haven't already, I'm a little behind on GitHub) would be great. |
They did (#3117), and I closed it, deferring to #132, which is about allowing statements in expressions in general, not just in switches. Because that'd be generally useful. We can also discuss allowing a terser syntax for switch statements here, which would be the other direction to go. |
@munificent thanks for reading through my whole post on the matter, I hope you all end up allowing switch expressions at the start of statements! For multi-statement case bodies, I'll go join the conversation on the relevant conversation now :) |
According to the official documentation,
However, the code below doesn't compile (DartPad):
The error message is
On the other hand, these code compile:
Why?
Note, in Rust, the equivalent code compiles:
The text was updated successfully, but these errors were encountered: