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

Clause separators #337

Open
gkz opened this issue Dec 10, 2024 · 11 comments
Open

Clause separators #337

gkz opened this issue Dec 10, 2024 · 11 comments

Comments

@gkz
Copy link

gkz commented Dec 10, 2024

Currently, the clause separator specified in this proposal is a semicolon:

const e = match (x) {
  1: f();
  2: g();
};

From what I can see, the history of using the semicolon comes from this GitHub issue, where the point made was that having no clause separator syntax (just a prefix when, now removed) would cause parsing issues. It was suggested that something like a semicolon could solve the issue. However, semicolons are not currently used as punctuation in any other expressions (other than nested within function or class expressions). They are used and associated with the syntax for statements in JavaScript instead.

Before we get into possible solutions, let’s look at prior art for match expression clause separators in other languages:

  • Comma , suffix: Dart, Rust, C#
  • case prefix: Swift, Scala
  • case prefix and semicolon ; suffix: Java
  • Bar | prefix: OCaml, F#
  • Nothing (newline): Haskell, Kotlin

Could we use a comma instead of a semicolon in JavaScript? Yes. The only difference would be that we would parse the body of each clause as an AssignmentExpression (requiring sequence expressions to be surrounded by parenthesis). This is what JavaScript uses in call expressions, array literals, and object literals.

Object literals provide a mapping from keys to expressions:

const obj = {
  1: f(),
  2: g(),
};

In match expressions, we provide a mapping from patterns to expressions. This is not completely dissimilar.

What would using commas for match expressions look like?

const e = match (x) {
  1: f(),
  2: g(),
};

I believe looking at the prior art of other languages, and the prior art within JavaScript itself, it is worth considering whether using commas , as clause separators would be a better fit than semicolons ;.

@ljharb
Copy link
Member

ljharb commented Dec 10, 2024

A comma would be confusing because it can already be used in expressions as the comma operator.

@gkz
Copy link
Author

gkz commented Dec 10, 2024

A comma would be confusing because it can already be used in expressions as the comma operator.

As I point out, it is not confusing in call expressions, array literals, and object literals, all of which use the comma as the separator. These all parse expressions as AssignmentExpression.

@ljharb
Copy link
Member

ljharb commented Dec 10, 2024

That's because those are lists of things. This isn't a list, conceptually.

@gkz
Copy link
Author

gkz commented Dec 10, 2024

That's because those are lists of things. This isn't a list, conceptually.

As I point out, an object literal is a mapping from key to expression. It defines a list of properties.
A match expression provides a mapping from pattern to expression. It provides a list of clauses.

I don't think these are completely different. It is definitely odd to use semicolons in this context, when otherwise they are used in the context of statements, which this is not.

@ljharb
Copy link
Member

ljharb commented Dec 10, 2024

Semicolons are used to separate class fields in a class body, which feels much more analogous to me here than an object literal.

@gkz
Copy link
Author

gkz commented Dec 10, 2024

Semicolons are used to separate class fields in a class body, which feels much more analogous to me here than an object literal.

What is your reasoning? How is it more like class fields than an object literal?

@ljharb
Copy link
Member

ljharb commented Dec 10, 2024

Because an object literal creates a "thing" (an object). A match block may resolve to a value, but it's not to create a thing - it's encapsulating code, just like a class body (or a class field initializer)

@gkz
Copy link
Author

gkz commented Dec 10, 2024

Every class field initializer is executed in order. E.g.

class A {foo = console.log(1); bar = console.log(2)}
new A
> 1
> 2

In this way it looks a lot more like a statement list.

This is not the case for match expressions, as the body of each clause is only executed if the pattern matches. It is a mapping.

In any case, these arguments ignore the prior art from other languages as well.

@haltcase
Copy link

haltcase commented Dec 10, 2024

Because an object literal creates a "thing" (an object). A match block may resolve to a value, but it's not to create a thing - it's encapsulating code, just like a class body (or a class field initializer)

This seems to me to be a philosophical difference between two groups of people:

  1. Those who equate a pattern matching construct to a hash map of sorts, a mapping from patterns (keys) to values. I believe @gkz sees it this way, so pattern matching being more like an object literal, with expressions separated by commas, makes sense.
  2. Those who equate a pattern matching construct to a function that performs logic and ultimately determines which value to return algorithmically. I believe @ljharb sees it this way, so pattern matching being more like a class/function body, with statements separated by semicolons, makes sense.

@ljharb
Copy link
Member

ljharb commented Dec 10, 2024

Yes, and every match clause is executed in order, like a statement list.

@gkz
Copy link
Author

gkz commented Dec 10, 2024

Dart, Rust, and C# all chose to use commas as their clause separators. They all also have statements that can be separated by semicolons, but didn't choose that for their match expressions. It's worth considering this prior art from other languages.

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

3 participants