Skip to content
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

Shorter switch statements #169

Closed
ANF-Studios opened this issue Dec 31, 2020 · 9 comments
Closed

Shorter switch statements #169

ANF-Studios opened this issue Dec 31, 2020 · 9 comments

Comments

@ANF-Studios
Copy link

ANF-Studios commented Dec 31, 2020

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.

@dustbort
Copy link

dustbort commented Feb 7, 2021

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.

@hax
Copy link
Member

hax commented Apr 17, 2021

@dustbort I guess @ANF-Studios suggested reusing switch keyword for pattern match, just like C# (or Java).

@ANF-Studios
Copy link
Author

Yes, kind of like switch expressions except they don't (necessarily) return a value, they are normal switch statements, but similar to it.

@AKST
Copy link

AKST commented Apr 21, 2021

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)

  • Using different keywords can make finding information about the seperate features easier (e.g. searching "case expression" over "switch statement") (source)
  • There's a desire for there to be zero overlap between switch statements and pattern matching (source)
  • Switch has baggage such as fall through (source) (this reason may be dated and tied to earlier version of the proposal)
  • There's a general design for switch statements and pattern matching to be treated as seperate constructs (source)
  • It makes the implementation of JS engines and JS tooling more complicated (no source, but just a guess)
  • No specific source, but there seems to be a general sentiment of yeeting the existing switch syntax into the sun and using a different keyword is part of that.

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 😄)

@ljharb
Copy link
Member

ljharb commented Apr 21, 2021

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.

@ljharb ljharb closed this as completed Apr 21, 2021
@nikeee
Copy link

nikeee commented Apr 22, 2021

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.

@ljharb
Copy link
Member

ljharb commented Apr 22, 2021

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.

@nikeee
Copy link

nikeee commented Apr 22, 2021

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.

@ljharb
Copy link
Member

ljharb commented Apr 22, 2021

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants