Hacker News new | ask | show | jobs
by Tloewald 2985 days ago
So the proposal is super confusing to me (but I hadn't seen match before). At first I thought it had something to do with regex.

Essentially it's a switch variant that is (a) confusingly named 'match' (bad) (b) returns a value (good?) (c) and is actually a kind of function (wha?), since it (d) supports recursion, but (e) doesn't require break (good), (f) looks like a bag of inline functions but isn't (bad).

How about provide a replacement for switch, with a name that isn't confusing that doesn't require break but otherwise has switch semantics, isn't recursive (we have a mechanism for that) and has the matching behavior suggested? Assuming we stick with 'match' it would look like:

foo = match(x) { case {y: 1} : /* result if x.y === 1 */; ... }

Seems far less confusing.

5 comments

Love this comment; reminds me of what I thought when I first encountered pattern matching.

a. Yup, that's what it's called in OCaml, Scala, F#, etc. Might be a little confusing at first, but it's all about finding a "match" for your value, which is different from a guard in an "if" or a "switch".

In an "if", you need to provide a boolean (or a value coercible to a boolean). In a "switch" you don't provide the boolean, but under the hood the switch is just iterating through and comparing your value to all cases: it computes the boolean for you by testing if two values are equal.

Finding a "match" is different, because you're not comparing values, you're also comparing structure. So for example you can do things like

match (v) { case { x, y: 3 } => ... case { y: 3, contents: { name: 'Foo', error }} => ... case { x, y } => ... }

And it wont just compare "v" against all cases, do a deep comparison, check the existance of certain keys and bind variables as necessary so you can use them on `...`.

b. More so than "returning a value" I like to think of it as "resolves to a value". Thinking of it as "returning" can be confusing since it might make it sound too much like a function.

Switch/if are statements. They control flow, and ask the computer to do something. Another statement is assignment; `var a = 0;` "does" something, but it doesn't represent a value.

Expressions like `3 + 2 + x`, `f(x)/2`, and `match({x:3}) {...}` represent a value that hasn't been computed, and then resolve to one.

c. This is why I prefer to think of it as an expression: cause expressions resolve to values. Function calls are expressions too!

d. If you're in a function, all expressions support recursion since you can mix them up. A

e. Awesome.

f. I think that's on purpose, because in a way each case acts a bit like a funciton. You can define "arguments" in it that get bound to values, and they resolve to another value.

> In a "switch" you don't provide the boolean, but under the hood the switch is just iterating through and comparing your value to all cases: it computes the boolean for you by testing if two values are equal.

Switch can also be implemented as a jump table in some cases.

I totally get what it does, even if I hadn't seen it before. What I don't get is (a) the use of the word 'match' (that's unfamiliarity) and (b) the proposed syntax which looks like an absolute mess, something worthy of PHP, and the fact that we end up with something that isn't a function but is recursive. Or something.

'x => x = 1' in Javascript, is an expression that resolves to a function. I do understand the difference, and it's important.

'match(x) { {x} => foo }' is not a bag of functions but a new use of the '=>' operator that is confusing/surprising. I can get over it, but why should I have to?

'match(x) { {x} : foo }' would be less surprising.

You're saying 'match({x:1}) { ....}' is an expression, but is 'match(x) { {x} => foo }'?, What does it resolve to? It smells like it either isn't an expression (just as you can't write x = switch(x) {...};) or it's a new kind of function.

Apparently they're overloading the meaning of the => "fat arrow" in the proposed syntax. I agree it's confusing because it's already used with lambdas in case of JS, but many languages with pattern matching do use it (eg. Scala, Rust). Other languages like Haskell and F# use the thin arrow -> instead.

Pattern matching can be exhaustive or non-exhaustive. Basically, if a match construct is going to be an expression it has to yield a value* for any input value, ie. there has to be a case arm for every possible input. Usually match blocks have a way to declare a "default" or "else" arm for those values that don't match anything else. Some languages with static type systems can statically figure out whether a given match expression is exhaustive or not. But in JavaScript's case that's most likely not an option. The JS way is probably yielding `undefined` if a value fails to match anything.

* or diverge, ie. loop forever, or exit via nonlocal means such as exceptions.

I believe all these languages, if they use some arrow in lambda syntax (so, except Rust), use the same kind of arrow for pattern matching. It's an obvious syntax choice when lambda arguments also use the same destructuring syntax: why would anyone want to remember which of those is `[x,y]->x+y` and which is `[x,y]=>x+y`?
Fair point, it does make sense.
Agreed that while "pattern matching" is a common term, it's perhaps not common enough. For comparison, a C# proposal of terser syntax (which uses the switch keyword) is available here: https://github.com/dotnet/csharplang/blob/master/proposals/p...
it is showing the function roots of the match statement.

The kind of people who will jump in and use it will be the people who are used to have it called match.

Better yet, looking up match statement will show how matches work in the functional approach.

I am super happy they are calling it match.

Huge fan of pattern matching and destructuring with rebinding in Elixir (where it actually goes far deeper), it makes code way more succinct. It seems like you haven't grokked it yet (or played with it by trying to write code with it and feeling out what it's actually doing for you vs. the alternatives)... read https://elixir-lang.org/getting-started/pattern-matching.htm... as well as https://elixir-lang.org/getting-started/case-cond-and-if.htm... where you will see that this proposed construct is almost the same syntactically as `case` in Elixir.

Changing `case` in JS to these entirely different semantics would break pretty much everything, which is likely why a new keyword was needed.

> Essentially it's a switch variant that is (a) confusingly named 'match' (bad)

You seem to have quite strong opinions with regards to what's good and what's bad considering you don't have enough experience to recognize language features which have existed for decades outside the JS environment.

Take a look at pattern matching in other languages like Scala. I find the proposed syntax strikes a great balance between being familiar (for people familiar with Scala etc) and using well understood js structures (destructoring)

Would love for this to land :-)

a match(x) {...} to augment switch(x) { ... } would be very welcome indeed. It would also get rid of the function scopes which imply actual function objects/closures which are not actually needed.

It would also remove the duplicity in the first example shown where match(res) matches on the statusCode field (I guess because `200` is declared as a literal ? while the variable is extracted out. Which also means you cannot match against variable content.

It would also retain a level of readability while the proposed syntax has no readability for the uninitiated. Doing that too much makes the language harder to read and learn for beginners.

My basic stance is that a language does not have to be perfectly concise but a good compromise between readability first and density second.

It is always nice to optimize a language for experts but the majority of users of a language such as javascript are not veterans.