Switch statements are really cool, but it'd be nice if they could be shorter for one-line-code-actions.
Say we have a switch statement:
switch (foo) {
case bar:
console.log("bar")
break;
case bar1:
console.log("bar1")
break;
case bar2:
console.log("bar2")
break;
default:
console.log("neither bar, bar1 or bar2")
break;
}
And for one line actions per case, this just gets overwhelming. I suggest that short switch statements get added.
An equivalent of that code above should look like this, shortened:
// Concept of what it would look like.
switch (foo) {
bar => console.log("bar1"), // Probably a comma to separate other statements or none at all requiring a new line instead.
bar1 => console.log("bar1"),
bar2 => console.log("bar2"),
_ => console.log("neither bar, bar1 or bar2") // An underscore for the default case or just the default keyword itself.
}
We can go one step further and inherit this from C#:
// Concept of what it would look like.
function foo(bar) {
return bar switch { /*Code*/ }
}
What this'll do is return bar (a variable declared somewhere) in a switch, as in.. a value depending upon the value of bar.
That'd make the code look a lot cleaner.
Specifically, what does this issue ask of the proposal?
The proposal's syntax already covers your example.
case (foo) {
when _ if _ == bar0 -> console.log("bar0"),
when _ if _ == bar1 -> console.log("bar1"),
when _ if _ == bar2 -> console.log("bar2"),
when _ -> console.log("neither bar, bar1 or bar2")
}
(This also shows that _ is not a _discard_; it is a scoped _binding_.)
What does your example say about fall-through semantics? Consider that the proposal's syntax using a guard allows for fall-through using ||, as in
case (foo) {
when _ if _ == bar0 || _ == bar1 -> console.log("bar1"),
when _ if _ == bar2 -> console.log("bar2"),
when _ -> console.log("neither bar, bar1 or bar2")
}
It's hard to imagine not having to do this sort of thing in the guard, since we need a way to distinguish between referring to an _already bound_ variable versus _attempting to bind_ a variable. The proposal's syntax makes it clear that the attempt to bind happens in the pattern match and the reference to bound variables happens in the guard.
@dustbort I guess @ANF-Studios suggested reusing switch keyword for pattern match, just like C# (or Java).
Yes, kind of like switch expressions except they don't (necessarily) return a value, they are normal switch statements, but similar to it.
Hey @ANF-Studios sounds like you're suggesting reusing the switch keyword, or possibly a different feature entirely from what this proposal is pursuing (Hard for me to tell). But if it's the former, the idea of upgrading the existing switch syntax has been brought up a number of times, and the champion (past & present) has outlined on various occasions why this proposal avoids doing this
Some of those reasons being (non exhaustive)
Here are some other links (also non exhaustive)
While there is a lot of excitement about pattern matching in JS & sounds like you share some of that enthusiasm, I'm not sure pursuing using a switch keyword in the syntax will be that productive, but hopefully the links I shared give you a better idea of the current direction of this proposal. (PS I have no affiliation with TC39 I just watch issues here 😄)
The proposal has been updated in #174; the readme hopefully makes it clear why we won't be proposing any changes to switch, and why pattern matching is explicitly intended to have as little overlap with switch as possible.
I understand why the decision went against re-using the switch keyword and I don't have the intention to change that. But some of the points don't actually address the proposal that @ANF-Studios made.
Switch has baggage such as fall through
C# got pattern matching pretty recently. They decided to re-use the switch _keyword_ for expressions and changed the syntax for that:
var result = someValue switch {
//...
}
instead of the classical switch(someValue). This allowed the language to define entirely different semantics for the case blocks. When using this _switch expression (not the statement)_, cases don't require a break _because they don't fall-through (in contrast to the switch statements)_. They are completely separate constructs.
It makes the implementation of JS engines and JS tooling more complicated
The C# team partly did it that way because inventing a new keyword may break existing code and requires special handling for backwards compatibility. For example, when the nameof keyword was introduced, they needed to handle the case where the code may also contain a function called nameof.
This may also be relevant for a match keyword. For example, with a proposed syntax that starts with this:
match (tree) {
````
The parser has to explicitly handle the edge case that there is a function called "match" as well as some look-aheads / speculative parsing respectively. Depending on the size of the argument of match, this might be a bit of work.
JS having automatic semicolon insertion (ASI) doesn't make things easier here. An empty match statement (if considered valid) would suddenly break compatibility:
let a = match (t) { }
// Is it "let a = match(t); {}"
// or "let a = match (t) { }"?
````
So I'd argue that inventing a new keyword is more complex than re-using an existing one, since keywords cannot be used as function names etc. The DX benefits must outweigh the complexity.
That they’re separate constructs, grammar-wise, doesn’t mean they’re separate in the minds of users, especially when googling for help and documentation.
The way the parser handles that case is with a “no LineTerminator here” (NLT) prohibition, which only causes a problem for those who want to write a match construct with allman style braces. However, that style is hardly ever used in js, and breaks with return, so I’m not concerned with it.
ASI has been responsible for some bad DX in the past. When designing a new feature, it would be beneficial not to make that feature prone to that again and hurt DX. Just using an existing keyword would make things easier and I think that this is what was proposed by ANF-Studios. It doesn't even have to be switch. One could even use case or any other keyword. The code that is inside the match expression then may contain new keywords because we can be sure that it doesn't interfere with existing code.
The approach I'm describing avoids ASI, which is the whole reason the restriction is needed in the first place. Additionally, the committee has explicitly decide that ASI hazards will never obstruct advancement of a feature - you should be using semicolons, not relying on ASI.
switch and case are nonstarters because they're part of switch statements. There are no other existing keywords that would be viable candidates.
The choices are thus "a new keyword, using NLT" or "no pattern matching in the language, ever". I prefer the former.
Most helpful comment
Specifically, what does this issue ask of the proposal?
The proposal's syntax already covers your example.
(This also shows that
_is not a _discard_; it is a scoped _binding_.)What does your example say about fall-through semantics? Consider that the proposal's syntax using a guard allows for fall-through using
||, as inIt's hard to imagine not having to do this sort of thing in the guard, since we need a way to distinguish between referring to an _already bound_ variable versus _attempting to bind_ a variable. The proposal's syntax makes it clear that the attempt to bind happens in the pattern match and the reference to bound variables happens in the guard.