Hacker News new | ask | show | jobs
by aylmao 2985 days ago
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.

2 comments

> 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.