Hacker News new | past | comments | ask | show | jobs | submit login
JavaScript Pattern Matching Proposal (github.com/tc39)
371 points by JONBRWN on April 25, 2018 | hide | past | favorite | 251 comments



Leaving aside syntax preferences or other superficial concerns, I find it discouraging that "Motivating Examples" for a proposal include direct references to particular libraries, or particular libraries' examples.

I mean, that one (of two) motivations for adding a feature to a language is "Terser, more functional handling of Redux reducers", just feels wrong and casual.


It's probably more of a "know your audience" decision. Anyone who's used an ML-derived language will understand the motivation for pattern matching. But a large percentage of JS developers don't really have much background in non-mainstream languages or programming language theory, so they chose an example that's likely to resonate more with that group.


> But a large percentage of JS developers don't really have much background in non-mainstream languages or programming language theory

Not to side-track the issue, but I keep hearing this. I'm wondering if this is true, and if so, how such a statement is verified. I'm currently a full-time JS dev, but have written my own programming languages, have professionally written in C, C++, C#, F#, Ruby, and others, and have dabbled in many other languages (and would love to professionally work in Clojure). [EDIT: Er, to the "non-mainstream" point, I've dabbled in Rust, Clojure, OCaml, Haskell, PureScript, Erlang, Elixir, and Elm. I don't know many JS devs that have done all of that, but most people I know have tinkered in odd non-mainstream languages.]

Most JS devs I know are not single-language devs. Am I just a super anomalous member of the JS community?

... Anyway, back on topic, I'm a big fan of pattern matching, so I'm all for seeing this proposal become a reality.


I think you're a super anomalous member of any programming language's community. I doubt even one programmer in a thousand has written F# professionally (not being hipster here — I haven't done it myself).


a LOT of function programmers end up doing a lot of javascript work.

I don't think he is odd in any way.


Sure, a lot of functional programmers probably do end up doing javascript work. But that's still a large subset of a very small fraction of programmers in general, and they're definitely still vastly outnumbered by the folks who are barely more than script kiddies.


Who are you calling a hipster....


It's certainly been my experience that there are a lot of JS developers who aren't like you, though I have met a few who are. But JS has traditionally been one of the most accessible languages. Before transpilers became common, view source allowed non-programmers to learn javascript by example. And because of it's use in the front-end/web, you saw a lot graphic designers and other non-programmers learn it to be able to put something online. Javascript also goes out of its way to be friendlier. While other languages complain when you try to add two different width integers, Javascript will happily add a number and a string together. It's something that drives those of us with a static typing background nuts, but makes the language more approachable overall.

It's certainly a tired narrative that a lot of the churn in the front-end javascript framework world comes from developers who aren't aware of well-recognized patterns and techniques from other languages. But stereotypes and tired critiques almost always have some basis in reality and it does seem like the JavaScript world is rediscovering a few lessons the hard way rather than learning from the experience of other language communities.


> Javascript also goes out of its way to be friendlier. While other languages complain when you try to add two different width integers, Javascript will happily add a number and a string together. It's something that drives those of us with a static typing background nuts, but makes the language more approachable overall.

We have differing definitions of friendly. Since I understand types and operator overloading, I know that 1 and "1" are different. I also know that the operator + does traditional math addition and concats Strings. If I didn't, it would probably drive me bonkers that "1" + 1 is "11" in JS instead of 2 or even "2". Having knowledge about types and overloading automatically tells me that String + Int = String.

I'm not saying that JS is bad since it lacks static typing. I'd argue that lack of static typing makes a language tougher to learn rather than more approachable. The barrier of entry is lower, but makes comprehension tougher.


> I'd argue that lack of static typing makes a language tougher to learn rather than more approachable

I think it's a bit of a wolf in sheep's clothing. It makes it harder to master, but less intimidating when you're first starting out.

Also, I probably shouldn't have used the term static typing, since the issues I'm talking about are more associated with weak typing rather than dynamic typing.


I think it's a stereotype from the old web when it didn't require as much technical skill as other programming fields. Most of my colleagues started in other languages and learned Node/JS on the job.


With JavaScript being the most used language in the world I highly doubt the old stereotypes still apply


Unless front-end development as a category has completely disappeared in the last few years there are certainly quite a few people who don't know how to do anything else.


Any language that is the most used is going to be used by mostly below-average programmers. I have yet to see a single domain where quality can be measured by popularity.


Is it the most used language in the world? Seems like I keep hearing comments like that, but the rankings I've seen don't list it that way.


It's the most visibly used language. But the world runs on a ton of code that isn't the web.


According to Stackoverflow's developer survey it is [1]. Doesn't surprise me since developing for web means JavaScript.

Curious to know what rankings you are referring to though.

[1]: https://insights.stackoverflow.com/survey/2018/#technology


I didn't see anything on methodology, but I'm assuming they're just surveying their users—in which case, I think it would make some sense for javascript to be overrepresented.


I'd be curious to see the methodology behind the rankings you speak of.

EDIT: saw the sibling thread.


What rankings have you been seeing? Javascript is on top of the list from what I've been seeing.

https://insights.stackoverflow.com/survey/2018/#technology


Tiobe index for one. But I just searched Google and checked the top few.


The TIOBE index has always been a bit weird. If you read their note about their methodology, you'll see that it's measuring something kind of interesting, but not really what you'd call "the most used languages." For example, IIRC part of a language's TIOBE ranking is how many college courses use it.


Do you know of another more objective source, though?

Other folks are citing Github and Stack Overflow, but they are just reporting on proportions used on their own sites, and I would not be at all surprised to find that both those sites are more popular with web developers than say Java or C/C++/C# or Visual Basic devs (all those languages outrank javascript on Tiobe).


That seems pretty likely to be true of Github. But for Stack Overflow, I would be really surprised. Stack Overflow has had a huge and thriving C# community from the start, and it's got so much page rank that questions about any language, "web" or not, are likely to lead there. The only languages that seem likely to be underrepresented are ones that are mainly used by people who learned them long ago and aren't gaining any more users (like MUMPS or something), since those people are unlikely to have questions.


At least according to Github: https://octoverse.github.com/ - it's in its own class compared to the others: 2.3m to the next most popular (1m, python)


Yeah, but there's a trivial selection effect going on with Github.


I think that the large percentage of JS developers the GP is talking about is people for whom JS is probably their first or second programming language (and then the first is something like python, ruby or php).


You are conflating two questions:

1. What percentage of JavaScript/front-end web developers are familiar with any other programming languages?

2. What percentage are familiar with a certain specific subset of other programming languages, namely functional or functional-ish languages with pattern matching as a core construct?

And you're treating an answer to (1) as an answer to (2).


I'm s full time JS dev too. People keep saying that JS sucks or whatever, but [Rust, Clojure, OCaml, Haskell, PureScript, Erlang, Elixir, Elm, Scala, Nim] jobs are just so rare that picking one and going deep is basically like hoping to win a lottery.


They should be included, as it simply is a valid use-case which most likely will actually be used. However, there should be many more examples or good real-world use-cases. Lack of those concerns me.


I think if you look at languages that support them natively, like F#, OCaml/Reason, or Elixir/Erlang, you'll find plenty of examples of real-world uses. Whether those would suffice to illustrate how to apply them in a JS codebase that has this proposal enabled, I cannot say.

However, having gotten used to having them, I can safely say I'll always feel hobbled to work in a language that doesn't have them, so I'm very much in favor of adding them to JS, whether or not this is the proposal that wins out over time.


I actually drew the opposite conclusion. As in, "here's an example of how a commonly used library is overly verbose and how it could be simplified with this language extension."


Usage of these libraries are not the reason this proposal exists, but rather demonstrating how the proposal fits with popular tools and such within the ecosystem.


Stage zero proposals generally read less formally. IIRC they are held to lower standards and expected to be formalized in the later stages.


I really liked the examples they used. I was able to share with coworkers who weren't familiar with the concept and it made perfect sense.


Absolutely, agree. The code examples should be vanilla code, which could then be expanded to the preferences of any library/framework.


Redux reducers were at least partly inspired by constructs of other languages that heavily rely on pattern matching.

So I'd take it more as "You know this mediocre compromise we had to do because JS is missing this core language feature? Well, there you go".


Well, think about the @ operator in python. It's aimed solely at people who use numpy.


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.


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.


I think it's good to note that this proposal is not far along the process. Pattern matching, if it clears TC39 (a tall order, to be honest), won't land in a spec for half a decade at least I would bet.

So, keep the feedback coming, and don't worry that you'll have to learn this syntax tomorrow. It's still very early, and I wouldn't be surprised if there are at least a couple major revisions before anything like this lands in ECMAScript.


With babel plugins/transforms this could be used a lot sooner than that


Babel has a lot of plugins, and hopefully people don't have to learn all of them. How many people use stage-0 plugins for bind operator or do expressions?

You're right that Babel (and TypeScript!) implement features earlier than they land in the spec, but at least TS does so only when it's almost for sure going to make it in (around stage 3) and people probably shouldn't be using stage 0-2 plugins for anything but experiments, IMO.


> if it clears TC39 (a tall order, to be honest), won't land in a spec for half a decade at least I would bet.

Given current speed of iteration I think you can cut that in half. The key will be implementations - Babel plugins help with that.


The current speed of iteration means you get updates in smaller batches rather than more throughput. Still important, but it's not like we're letting proposals bake less. Note how so far we've only gotten two big new features post-ES6 - async functions and async generators. Further, some features (e.g. decorators, bind operator) have had babel plugins for years and yet hasn't shipped in a spec.


True. Mostly due to hold ups in the Stage 3 — Stage 4 process I think


Pattern matching would be a great addition to JS. I built a pattern matching library[1] with a very similar syntax back in 2015 (although I will be the first to admit that it's a naive implementation). It makes validating/traversing deeply-nested objects much less verbose. I'm excited to see where this proposal goes.

[1] https://github.com/cshepp/Kasai


Shameless plug but here is my pattern machter [1] :D

Example:

    // simple factorial
    const factorial = n => match(n)
        .when(0, 1)
        .otherwise(n => n * factorial(n - 1));

    // walking a tree
    class Tree {
        constructor(left, right) {
                this.left = left;
                this.right = right;
        }
    }

    class Node {
        constructor(value) {
                this.value = value;
        }
    }

    const T = (l, r) => new Tree(l, r);
    const N = v => new Node(v);

    const walkT = t => match(t)
        .when(Node, v => console.log(v.value))
        .when(Tree, t => { walkT(t.left); walkT(t.right)})
        .otherwise(_ => 'error');

    const mapT = (f, t) => match(t)
        .when(Node, v => N(f(v.value)))
        .when(Tree, t => T(mapT(f, t.left), mapT(f, t.right)))
        .otherwise(_ => { throw new Error('error') });
Works also on deeply nested objects.

[1] https://github.com/MarkusPfundstein/pmatch-js


Method-based syntax for pattern matching is easier to read and grok (IMO), but it lacks the dynamic capabilities of data-oriented syntax (in Kasai, patterns can be built/manipulated at runtime). It's interesting to compare the two approaches.


If you mean by method-based pattern matching that you evaluate a function for each 'case'. My lib can do that do:

    const match = require('pmatch-js')
    const _ = require('lodash')

    const fizzbuzz = x => match(x)
      .when(a => a % 3 == 0 && a % 5 == 0, 'fizzbuzz')
      .when(a => a % 5 == 0, 'buzz')
      .when(a => a % 3 == 0, 'fizz')
      .otherwise(a => a)

    console.log(
      _.range(1, 101).map(fizzbuzz).join(' ')
    )
But maybe you mean something totally different :-)

EDIT: Ah I think I know what you mean. Your lib takes an array of tuples to define the patterns. Sorry for the confusion.


Wow, importing the whole lodash to use just one function? There’s es6 way of getting an array with a range of numbers:

    [...Array(100).keys()].map(v=>v+1)
A little bit longer but no dependencies.


Although this method is pretty, I see 4 loops in this small line. a better way would be to only import the range method from lodash like so :

    import _range from "lodash/range";


Pretty cool trick mate. Didn’t know


I rather like your syntax compared to the proposed version. It is less concise but must more consistent and parsable:

  return match(user, [
          [{first: $, middle: $, last: $}, (f, m, l) => f + ' ' + m + ' ' + l],
          [{first: $, last: $}           , (f, l) => f + ' ' + l],
          [_, 'unknown']
      ]);


I think that this is not always guaranteed to work, because {first: $, middle: $, last: $} is the same as {first: $, last: $, middle: $}

... so calling Object.keys() is not necessarily going to use a consistent ordering. I think that is why they have to use the more verbose syntax in the other library.

I think you could do something like: {first: $.0, middle: $.1, last: $.2}, (f, m, l) => ... pretty easily, though.


I believe in es6 Object literals are guaranteed to keep their ordering, and Object.keys() will iterate through them in the order defined.


The ordering of Object.keys() is equal to insertion order of the keys into the object. So if you have an Object generation function, it can insert the keys in the proper order (and take advantage of stuff like v8 hidden classes)

http://2ality.com/2015/10/property-traversal-order-es6.html


Nice library! This actually looks great. The syntax is a bit noisey, but tbh not any more than the actual proposal.


Interestingly, the match construct is an expression, which I don't think JavaScript has m/any of. Perhaps they could retroactively make if/else expressions, if that doesn't break back-compat.


"do" expressions have been proposed as well: https://github.com/tc39/proposal-do-expressions

Babel supports it if you want to live on the bleeding edge: https://babeljs.io/docs/plugins/transform-do-expressions/

It would be kind of neat if JSX implicitly used "do" expressions in children expressions, though the ternary operator is already very useful in JSX, and more concise [1]

1. https://prettier.io/playground/#N4Igxg9gdgLgprEAuc0DOMAEBhCB...


`request.statusCode == 200 && { body: {} } || request.statusCode == 404 && 'Not Found' || throw new Error('Something happened')`

This is basically what they could translate into without having if/else or nested ternaries.

I'm not sure if it is much better than either but I like it more.


That would be really cool. This match feels a lot like Rust's which is good.


I would argue that it doesn't feel like JavaScript much which seems bad.


Why? To me it seems pretty aligned with the "feel" of destructuring.


In my mind there are several valid options out there now:

1. Regex style: new match(input, options);

or built in function match(foo, bar);

or like switch match (foo){ cases

}

But, to have the switch statement syntax and return a value seems like a less than good way to implement this.


I'd argue they've already started going down the "doesn't feel much like JavaScript" road when they started introducing things like fat arrows. I agree that it is bad.


Along with the class syntax (however convenient, JS is not a class-based language).

Aren't decorators also in the works?


Then you would like to have statements as first class objects (e.g., to build them and pass around) and you are one step away from monads of Haskell.

Then you HAVE TO HAVE types, otherwise reasoning about effects in these constructed statements would break your mental spine.

Blasphemy.


The ternary operator ?: is an expression, but for expressions containing statement blocks, you have function() {} and () => {}, including the immediately invoked function expression, (function() {}())


Javascript already has a conditional expression, the ternary operator: `condition ? expr-if-true : expr-if-false`.


It doesn't have code blocks (aside from self-executing lambdas) and is very hard to reason about when nested.


What do you mean by expression? In the common usage, javascript has plenty of them. E.G. "asdf", 1 + 2, [3, 4], {"foo": "bar"}, and fn(arg).

Not to mention the if/else expression, a ? b : c


I can only see this being a foot gun. Deep equality testing of objects is going to encourage the use of getters. Getters with side effects (no way to prevent them) will basically ruin your day if you try to use pattern matching with them.

Additionally, this would be the first "native" way to do deep equality testing of objects. I can see it being abused to do simple one-off checks that could otherwise have been done more simply in an imperative way.

Do we really need to save the five lines of code at the cost of new syntax? This doesn't seem to prevent any significant amount of toil, since it can be pretty easily unrolled into existing JS syntax.


I wouldn't necessarily say it's deep equality, it's just a less verbose way of sequentially checking properties. It's not going to check every property.

At least for me, I feel I'd be using this a lot for the boring but common use case of checking for empty arrays and such:

  const doSomethingToArr = arr => match (arr) {
    [] => whatever,
    [x] => whatever,
    [x, ...xs] => whatever
  }


I'm guessing you've not worked with pattern matching of the kind you see in F# or Elixir before? Yes, JS would definitely be better off having this available. Well-developed pattern matching can actually obviate the need for conditionals in a lot of cases, which makes for far cleaner code.


If it is five lines of code vs. one line of code, and we believe that the number of bugs is proportional to the number of lines of code, irrespective of which language we are programming in, then this would be an obvious benefit.

And it isn't four lines saved, but four lines times the number of uses in the entire code base.


> we believe that the number of bugs is proportional to the number of lines of code

Why would you believe that?

And if you do believe that why don't you use a code golfing language?


What exactly are you contributing by taking what they said to the extreme?

Should someone now have to point out to you that a million-line function is worse than a ten-line function for calculating fizzbuzz? Is that meaningful discourse in your book?


My response seems to have upset you. I'm sorry that happened.

In general I believe that number of lines of code is correlated with number of bugs, but I would hesitate to say that it is proportional to. Going in with the explicit goal of reducing the number of lines could easily lead to more bugs, not less.


I would agree, BUT code needs to be readable first. The proposed syntax applied to e.g switch would have just braces which would not look very readable.

Having less lines can make extremely unreadable code. Readable code with an error is easy to fix, unreadable code is not.

At the very least the ambiguity of doing TWO things (matching AND destructuring) should not be allowed since it changes the outcome based on whether a value is a literal or a variable with the exact same literal assigned to it (at least that's how I understand it from reading the examples)


Isn't pattern matching interesting only in a strongly typed environment where the compiler can statically check that you are giving instructions for all possible cases?


While it's obviously safer and super helpful to have a compiler guarantee that there are checks for all cases, having a non-exhaustive pattern match call still be a big improvement over imperative checking of specific fields for control flow.

What I've seen doing fairly fast and loose prototyping and product iteration in Elixir over the last 2 years, is that patten matching can help create better boundaries and data guarantees within a codebase. A bug might still cause a runtime exception, but investigating and fixing the bug can be wayyyyy less painful than in JS, Ruby and friends when a nill/null/undefined/some-other-garbage can be passed very far through the stack before it eventually causes an issue.

Having a pattern match in the code, even a weak, non-typed pattern match on literals or basic data types, can act as an assertion in your code. "This is what the data needs to look like here if anything after is going to work". I think there is a probably an antipattern in taking this too far, coupling all sorts of your codebase to lower-level data structures There are better controls and safety mechanisms that can be implemented, but it can help a lot when there are specific parts of a codebase that are hotspots for issues, or where you're just trying to isolate where a data inconsistency is originating.


> Having a pattern match in the code, even a weak, non-typed pattern match on literals or basic data types, can act as an assertion in your code

Exactly. That's one of the key points I make when talking about Erlang: the = sign specifically, and pattern matching more broadly, are like having full-time, production assertions everywhere.

One key to the success of that in Erlang/Elixir, of course, is that you have the infrastructure and language support to manage widespread assertions that can fail.


As well as erlang, see also racket/match, perhaps the most comprehensive implementation of pattern-matching anywhere: https://docs.racket-lang.org/reference/match.html


For a great example of its use in a dynamically typed language, see Elixir.


No, have a look at Erlang! Pattern matching is one of it's most powerful feature.


And Prolog as well, which strongly influenced Erlang's design. (The original Erlang was written in Prolog.)

I agree with the sentiments of some of the other commenters -- it's much less troublesome to match on tuples (Erlang) and predicate clauses (Prolog) than it is to match on JavaScript objects. With tuples/clauses, the constructor syntax mirrors the pattern matching syntax, so it's trivial to read the code at a purely syntactic level to debug pattern matching issues, and this would simplify automatic static analysis as well. As soon as you can add "fields" to an object at arbitrary (non-local) points in the code, local syntactic analysis becomes intractable.


No, not at all. E.g. Erlang is built around pattern matching as a core feature even though it's an untyped language.


Erlang was built from the ground-up with pattern matching in mind, so it works. Instead objects are jammed into the pattern matching system, and where the two disagreed, pattern matching won. (Indeed, Erlang doesn't really even have objects, just some object-like convention.)

In a language that started with an object model that has grown a lot of features, trying to jam pattern matching into it after the fact grows a lot of corner cases fast. For instance, consider:

    a = {x: 1}
    a.toString = function() { return "magic: " + this.x }
Should a pattern match with the string "magic: 1"? Well... probably, because it turns out that "magic: 1" == a is true. But if we do that, how does the pattern match tell a is not in fact a string, if we want to do that? You can answer that question, but the answer will raise further complications of its own. Or you could build a pattern match system around ===, which will raise its own issues. And goodness help the pattern matching syntax if it decides that both are too useful to ignore (a defensible position) and now the syntax needs to support both....

I'd say this proposal is about 10 times too short to be useful, and by the time it's long enough to be useful, it'll be clear that almost nobody will want to learn how to take the already sloppy Javascript equality situation and then learn how to lay down a pattern matching language on top of that.

By contrast, since Erlang was built on pattern matching from day one, = and == have almost no such questions about them. It is very clear what they do. I did have to check what 1 == 1.0 was, even after years of use of Erlang, since I never used floating points in Erlang to speak of. (It is true, which technically I find a bit weird. I'm not sure there's another example of values of different types that can be equal. Note "" == [] because double-quotes are defined as producing lists and strings aren't actually a type, so that's not an exception.)


I believe Erlang inherited its pattern matching from Prolog term unification since Erlang was prototyped as a DSL using Prolog's op built-in predicate (plus other Prolog parsing DSLs such as definite clause grammars).

[1]: http://www.swi-prolog.org/pldoc/man?predicate=op%2f3


Heh, OK, Erlang was built for pattern matching from... uhh... the water table up? The mantle up? :)


However Erlang does include a static analyzer (dialyzer), which every production system should be using.


That is why I think we need some way to identify pattern matching + exhaustiveness checking, vs non-exhaustive switching matching is really more like "destructuring with syntactic sugar".

Exhaustiveness checking is what elevates pattern matching to the other side of the expression problem, which is about getting feedback from the compiler to help you write programs.


Hopefully this could be integrated with typescript as well, but this proposal would be great in certain cases to replace JS switch statements while also cleaning up the look of "case:" "break;" with something more elegant


Pattern matching seems to be very trendy/popular right now. It's a big jump from JS, but https://reasonml.github.io has very nice pattern matching that you can use, and it integrates with the JS ecosystem very nicely.


It's been "trendy" since the 70's. It's more that more people have experience with it and realize that pattern matching is like conditional statements on steroids.


> It's been "trendy" since the 70's.

It's been possible since the '70s but I've seen a lot more talking about it in the last 5-10 years.


Yeah, this applies to all that old Lisp features.


I think of full-featured pattern matching as a fairly recent addition even to Lisp. Lisp has had some pattern-matching constructs forever of course (cond, destructuring-bind, Norvig's sexp matcher from PAIP [1], etc.). But it's only with the more recent emergence of optima [2] as a de-facto standard that it now has really good pattern matching. It was probably the #1 thing I missed in Lisp, after having used ML a bit, until optima came along.

[1] https://github.com/norvig/paip-lisp/blob/master/lisp/patmatc...

[2] https://github.com/m2ym/optima


The first embedded language for programming with patterns for Lisp was implemented 1962 by Daniel Bobrow.

https://dspace.mit.edu/handle/1721.1/6106


AFAIK pattern-matching is not a historical Lisp feature, it's usually been limited to simple destructuring.

In the modern acception of tree patterns (as opposed to text patterns aka regular expressions), I guess it comes from ML (and possibly prolog but prolog's unification goes even further?): it doesn't look like ISWIM had tree patterns and I can't find older references.


In Lisp pattern-based programming has been first implemented in 1962 in by D.Bobrow (METEOR). From then on there are many implementations of pattern matching in Lisp based software, from Planner, to rule-based systems, LISP70 (Tesler, ...)...


This makes me sad. As somebody who started a CS degree back in the early 80s (finishing in the late 80s), and who had a few glimpses of languages which didn’t suck, the switch down to “everything is an 8086 running C code, if not x86 assembler” was an incredibly destructive event in this industry.

I was going to specialize in AI in my major, but I guess it was about 25 years too early. But that’s another tangent.


> AFAIK pattern-matching is not a historical Lisp feature

Aside from full packages like what lispm mentioned, implementing pattern matching (and later, full Prolog-style unification) in Lisp is a very common exercise in beginner Lisp textbooks, going back decades.


I'd say it's less about Lisp and more that the ML lineage is finally getting its day in the sun (and not before time).


Fair point. What I was trying to express was that it seems like the JS ecosystem has really been pushing and encouraging functional programming in a big way, which speaks to your idea that more people have experience with it.


IIRC, a big blocker to adopting ReasonML for a lot of people is its current lack of async / await support: https://github.com/facebook/reason/issues/1321


Looking at this as someone who's been writing a lot of ClojureScript lately, I'm reminded of how nice it is to be writing in a Lisp: if I felt this was the right syntactic construct for a common-enough problem in my codebase, I'd write a macro for it and get on with my life without having to wait for it to trickle through committees, compilers, and browser implementations.


And the next person to maintain your codebase would be grateful for your choice and insight, I'm sure.

Permissiveness is good for small projects but it can feel like getting into a suit tailored specifically for everyone else as projects get bigger.


I don't think this snarky remark is warranted. I think the point was that in Lisp-style languages (such as Clojure/ClojureScript) new features such as pattern matching can easily be added without "changing the language", as libraries. This is how core.async was added to Clojure, to pick a non-trivial example. Or more to the point, how core.match works.

Additions such as those do not have to be one lonely programmer's macros, they can be libraries widely accepted by the community.

I had a similar thought when reading the ECMAscript extension proposal: I'm glad that in the languages I use, features like that can be provided by libraries.


Yes, but I think the other point is well-made, as well: languages that allow this can be very difficult to work in if you have to maintain a large app with multiple contributors over a long period of time.


The same can be said for function calls, with the indirection they create hiding details of broken and side-effecting implementations.

None the less, the opposite is also true. Languages that have macro facilities can aid in writing more legible code. (See `threading` in clojure), or the `loop` macro and regular expression macros in common lisp.


Well said. Rambling Java code with little to no abstraction is its own kind of nightmare.

Some people are just terrified of any new abstractions, I guess, preferring to work with an endless series of tally marks, rather than these obfuscating “multiplication” and “exponent” complications (exaggerating to make a point - abstract != unintelligible).


I think there is probably some middle ground between "no abstraction at all" and "literally anything goes."


LCD?

Alas, that tends to be the steady state for Enterprise development :-(

(I know that’s not literally what you said, but that’s where that “middle ground” attitude leads to: the bottom)


I did say if. Macros often aren't the right choice, but there are times when they're appropriate, and on the premise that the new syntax would be good and responsible, it's great to not have the headache of monitoring compatibility tables for 2+ years.

When you get to the scale of enterprise software, though, I'd agree with you that the best thing to do is probably not to allow any macros at all. The inevitable abuse at the hands of inexperienced developers would quickly overtake any gains from more responsible macros without perhaps some clever and/or toilsome code review processes.


Honestly I'm not sure the Javascript story here is much worse - Babel tends to allow you to opt in to proposed specs pretty early, without worrying about browser support because you're not shipping it as is.

The benefit is that your polyfill/conversion to something browsers support today is handled by someone else, and your actual code base can point a reader to the proposal to at least make some sense later on.

Not that I imagine many people would encourage using a stage 0 proposal, but it's probably a bit better than everyone writing their own adhoc implementation.


Clojure's multimethods covers all of the use cases for pattern matching and then some. I've never felt the urge to reach for pattern matching when writing in Clojure.


some-> (threading macro) and the fact that you can put an s-expression _anywhere_, these two language fundamental features alone put lisps way above c-style languages. You can laugh and continue sipping your espresso while reading the JS community bicker about syntax changes and what you are allowed to type.


I really like this. The syntax looks a little weird at first, but it's actually a very nice complement to modern JavaScript's object destructing mechanism: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...


Its an interesting concept but, The choice of syntax seems bad.

The definition bit looks like a function definition but behaves entirely differently. Why? Why not make it a constructor like everything else? People could look at it and be like "oh a match object" instead of wondering if you overloaded function.

The selectors look like json, only assignable. Again make it obvious.I mean we could even just make it json why not.


I made the same type of arguments about fat arrows but everyone told me to stop whining and I'd get used to it. Years later fat arrows still feel like a bad syntax choice.


Really? I quite like them. I did get used to them in C# first, though. What would you have preferred?


From a language design perspective, fat arrows make parsing unnecessarily complicated because you cannot distinguish "(a, b, c)" from "(a, b, c) => ..." without looking ahead or backtracking. Certainly not difficult to solve, but this choice makes extending arrow syntax more difficult and the fact that a production called `CoverParenthesizedExpressionAndArrowParameterList` exists in the language grammar is a pretty ugly IMO


I would have preferred a word.

const my_arrow_function = arrow_function(){}

arrow_function my_arrow_function(){}

arrow_function(){}


In older C#, that would be delegate:

    delegate(int x){ return 10; }
(But yeah, nowadays that would be:)

    (int x) => 10


And they are ready to overload them here :/


You know that quip about C++ being an octopus made by nailing extra legs onto a dog?

Is the same phenomenon somehow tasteful when it comes to ECMAScript {({..., sy: nt => ax, ...}({,})} ?


I think C++ is the best example of where JS is heading.

We have different ways of doing the same thing, and you have to know the "right" and "wrong" ways based on whatever was the latest proposal.

We have fringe proposals that are just based on something cool some other language did, and they might stick or they might not.

We'll soon have the really obscure constructs that nobody uses except for this guy on the 3rd floor, and he's really vocal about it so watch out.

Maybe we'll even finally get macros in JavaScript [1] soon.

1: http://peter.michaux.ca/articles/macros-in-javascript-please


I feel like JavaScript is already a sort of functional language, so going in this direction is ok.

It's the object/class additions where I think it's all going a bit C++.

Start with prototypical inheritance, but with a Java-style `new` keyword that makes everything confusing. Next bolt on classes on the one hand, while correcting your prototype system `Object.create` on the other.

R has 3 different object systems and none of them are any good. How long before JavaScript catches up?


I think that one of the reasons that classes were added to the language was that people kept implementing them anyway, so it was better to standardise. This may be a good thing: JavaScript arguably has to be multi-paradigm, more than other languages, because the user-base is so diverse.


Surprised nobody has mentioned zkat's "fork", which contains a lot more detail:

https://github.com/zkat/proposal-pattern-matching

zkat (who is also an npm employee) is a leading committer to the tc39 version, so I think there's some chance that many aspects their version will be adopted into the proposal.


If I do the following:

  let visFilter = 'foo';
  const newState = match (action) {
    {type: 'set-visibility-filter', filter: visFilter} => console.log(visFilter)
  }
What happens?

Is it checking if action.type === 'set-visibility-filter' && action.filter === 'foo' or is it destructuring the value of action.filter into visFilter?


Every example in on the readme demonstrates that using an identifier in that position becomes destructured assignment.


found the answer in the full proposal. Variables will always be assigned to, never checked for equality.

To me this is pretty average and seems like it will cause magic numbers/strings/etc if I can't use constants where it makes sense. The proposed solution is to do something like

  { status: 200 } if (x === foo) => // do something
which just seems (for this type of use case, maybe not all) like more boilerplate code for no real benefit over just doing something like

  if (res.status === 200 && res.x === foo) //do something
I would much prefer if there was some sort of special operator used to either check equality or to destructure when matching so it is obvious what the code is doing.


Javascript is such a mess, this proposal looks really bad, and the guys discussing it are saying things like: "we should make sure that this doesn't look like anything else, so that people who learn the language don't confuse match and switch..."

There is also a lot of "we shouldn't break that" messages, and people reply with "oh we already screwed that up here and there, so it's fine"...


It looks pretty much exactly like the Rust match, though.

https://doc.rust-lang.org/stable/book/second-edition/ch06-02...


I don't understand what all the fuss is about. Just looking at the syntax examples given, it looks really nice and I would jump at the opportunity to use it.


as someone who only has a little experience with pattern matching, how does this look bad? The syntax looks good enough to me that I could and would want to use it.


“looking bad” is a really subjective thing.

Here is the syntax for different languages:

- Rust https://doc.rust-lang.org/stable/book/second-edition/ch06-02...

- Haskell http://learnyouahaskell.com/syntax-in-functions

- Scala https://docs.scala-lang.org/tour/pattern-matching.html


I just want to have ? Functionality in js like Ruby...

So I can do

let name = obj?.data[i]?.name;

And not get error can't read property data of undefined, when there is something wrong, just have undefined as value.

To fix this I've to write redudant checks for each level of nested property. Or do stuff like (obj || {}).prop....


This proposal will increase code coupleing by fixing the object structure, this will lead to refactoring problems and so on. A better approach would allow to match objects by their fileds while leaving the structure of the object unspecified.


Can you elaborate? What do you mean "match objects by their fields"? How does the proposal not already do that?


Maybe I did not express my self correctly, what I meant was - in it's current state the proposal rquires, when matching against an object, to specify all it's fileds, say you have an object with keys a, b, c then even if you match soley against the values of a , b you are required to specify c and by doing so notify it's existence. This poses a problem , since if you now remove c or add a filed d then the match will break failing to the default case , this means failing silently the worst that can happen. The described situation will be more then common since objects are ubiquitous to modern day js. Sadly nobody noted the existence of such a design flaw , which in my opinion is rather big since the proposal in it's current state implicitly turns objects into some kind of types , which they are not, such transformation will result only in increased coe coupleing , since anyone who matches against an object basically specifies it's type and as a consequence only doom and despare will follow to anyone involved.


> in it's current state the proposal rquires, when matching against an object, to specify all it's fileds, say you have an object with keys a, b, c then even if you match soley against the values of a , b you are required to specify c and by doing so notify it's existence

I think this is wrong. The proposal says that a pattern like `{x}` matches if the object supports `ToObject` and if its `x` property is not undefined. It doesn't say anything about requiring that the object have no other own-properties. This is consistent with how destructuring already works in JS (it ignores any extra properties).


I'm wondering whether types would be required to implement static analysis to see if a particular scenario in the match is not handled. Haskell has the ability to generate a compile error if you don't have a match specified for each possible permutation. This makes it easy to make sure you handle all possible inputs. This probably wouldn't be possible in JS. Probably the best that could be done would be if the default matcher was left off.


The example seems to be a dictionary, so of course you cannot guarantee that all possible permutations are covered.


I would prefer `match { ... } (value)`, with the `match` keyword essentially creating a function that does the matching when it invoked. Minor seeming change, but then you can think of match as being a generalization of arrow functions instead of a special new language construct (i.e., `(...args) => ...` is just shorthand for `match { ...args => ... }`)

I can understand why though they went with similar syntax to a switch statement though


This might be too cute, but you could support both unambiguously: match (subject) { ... } and match { ... }.


This wouldn't work if the match contained eg. break or return.


Yes, if match is an expression than this restriction is the correct design in my opinion


Could someone please explain what this could be used for ?


Instead of using if or switch statements to compute a value, you can use a prettier interface.

So this:

  function getStatus(status) {
    if (status === 200){
      return "Success";
    } else if (status === 401){
      return "Fail!";
    }
  }

  var myString = getStatus(response.status);
Now becomes this:

  var myString = match (status) {
    {200} => "Success",
    {401} => "Fail!"
  }


More like:

  var myString = match (status) {
    200 => "Success",
    401 => "Fail!"
  }


Smooshes the evaluation of an object that can be many different shapes into an easy to understand operation.

I think the HTTP response is a great example. It can have many status codes which can determine how you want to proceed with that response. If you've worked with handling HTTP responses, you've most likely implemented some version of a 'match' operation before.


Is not `switch` enough for this?


It's more concise than switch, as well as more powerful (can destructure objects). You can't set things equal to the result of a switch. I find the pattern very convenient.

  let day;
  switch (date) {
      case 1:
          day = "mon";
          break;
      case 2:
          day = "tues";
          break;
      default:
          day = "wed";
          break;
vs

  let day = match(date) {
      1 => 'mon',
      2 => 'tues'
  }


In fairness I think that would look a bit more like this:

    const getDate = (date) => {
      switch (date) {
        case 1:
          return 'mon'

        case 2:
          return 'tues'

        default:
          return 'wed'
      }
    }

    let date = getDate(1)
Which is, of course, still less terse. What I normally use when I have cases like this is an object-as-a-map. IE:

    const dates = {
      1: 'mon',
      2: 'tues'
    }

    let day = dates[3] || 'wed';


By the way, I don't think it is a good idea to write a function as a constant. Please compare this

    function getDate(date) { .. } 
to this:

    const getDate = (date) => { ... } 
The first version is more readable. We instantly see that it is a function and in a second case it looks like a constant at first. Also without the equal and arrow sign it looks simpler.


Life is more fun with fat arrows sometimes

    new Promise(function (resolve, reject) {
        methodOne(data, function (error, response) {
            if (erorr) {
                reject(error);
            } else {
                resolve(response);
            }
        })
    })
vs

    new Promise((resolve, reject) => methodOne(data, (error, response) => error ? reject(error) : resolve(response)));


Yes, there are cases when arrow functions are useful: when small functions are used inline, like this:

    var numbers = [1, 2, 3, 4].map(x => x * x);
    var bestUsers = users.filter(u => u.getRating() > 100);
But for a case when you have a large non-anonymous function, `function` keyword suits better. You don't need to use `const` keyword just becase it is something trendy now.

In your example, the code with arrow functions is smaller, but it is not more readable. Because there is no indentation, it is difficult to understand how code is nested. I cannot read that.

It can be rewritten using `deferred` pattern:

    var deferred = new Deferred;

    methodOne(data, function (error, response) {
        if (erorr) {
            deferred.reject(error);
        } else {
            deferred.resolve(response);
        }
    });

    return deferred.getPromise();
This way we can get rid of a callback in the Promise constructor. Please note that our code now looks sequential and we clearly see what happens after what. Asynchronous code is difficult to write and read; therefore we must put an extra effort to make it easier.

In my opinion it is generally bad idea to nest more that 1-2 levels of functions inside each other.


> A Deferred object is returned by the obsolete Promise.defer() method to provide a new promise along with methods to change its state.

> Starting from Gecko 30, this object is obsolete and should not be used anymore. Use the new Promise() constructor instead (or use the above backwards/forwards compatible Deferred function given below). For example, the equivalent of

Seems like it's obsolete. [0]

If you want to write async code, use async / await.

    const run = async () => {
        const response = await new Promise((resolve, reject) => methodOne(data, (e, res) => e ? reject(e) : resolve(res)));
    };
If you spend enough time with fat arrow, it's as easy to read as `function` is. On top of that, IMO it looks cleaner. It also allows you to do scope binding in a different manor which in React is much better.

This:

    <a onClick={this.onClickEvent.bind(this)}>Click Me</a>
becomes this:

    <a onClick={(event) => this.onClickEvent(event)}>Click Me</a>
[0] https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_...


> Seems like it's obsolete.

Then you can write your own Deferred implementation. I didn't even know it was implemented in browsers.

     const run = async () => { ... }
How to read this? run is a constant that equals the result of calling function async() thas maps to something in curly brackets? This is confusing.

> If you want to write async code, use async / await.

That is a good idea, but it has nothing to do with arrow functions.

> It also allows you to do scope binding in a different manor

`this` binding in JS in classic functions is broken by design, so yes, that is the advantage of arrow functions.


That one liner has a lot going on. The first one is easier to read.


Please also look at my idea how to rewrite the code for readability using `deferred` pattern: https://news.ycombinator.com/item?id=16933983


no


But function getDate(date) is hoisted. The const version isn't.


I don't think it is a good idea to rely on such subtle differences that are not well known among developers. We want to write code that is easy to maintain, not compete in knowing ECMA specs, right?


From looking at the proposal, there are substantial differences. The biggest is that switch is an equality test on a single value, while pattern matching as proposed allows matching on multiple properties of an object, as well as conditional clauses.

I need to look at it more closely, as some languages have matching be an expression, rather than a statement or block like the conventional switch.

It doesn't add something that you can't already do- tcomb and other libraries offer functions that mimic it- but it changes the way you can express certain ideas without needing them, in a way that is already familiar from other languages.

EDIT: yes, it does look like an expression, which means it can be used in many more places than a standard switch.


> Smooshes the evaluation

I see what you did there...


Pattern matching is very common in ML languages and is a core part of Rust.

As another commenter said, it's like conditionals on steroids.


Pattern matching can be quite powerful, e.g. in C# (which I wish was more indepth / more featured, it still feels a bit immature :( )

```

    switch(foo)
    {
       case TextBox t:
          Console.Writeline(t.Text);
          break;
       case TextBox when t.Text = "Bob":
          Console.WriteLine("Hello Bob");
          break;
       case Combobox c:
          Console.WriteLine($"{c.SelectedItem}");
          break;
       case null:
          Console.WriteLine("Ooops, null!");
          break;
       case int i when i == 5:
          Console.WriteLine("got an int, and it was 5!");
          break;
       case default:
           // handle the default case.
           break;
     }
```

IN languages like F#, pattern matching can compile time check you have covered all bases as well.

e.g, this won't build

````

    type VariableResult =
      | E of string
      | V of string

    let result = V "variable"

    match result with
      | E e -> printf "was error"
```

As i haven't told it how to handle the V case for that discriminated union. So you get nice compile time checking.

Ugh, how do you format code?


> Pattern matching can be quite powerful, e.g. in C# (which I wish was more indepth / more featured, it still feels a bit immature :( )

C# doesn't really deserve the name of pattern matching, just a type-based switch (with guards), you can't match on values let alone destructure them.

> Ugh, how do you format code?

4 spaces indent.


Basically simplifying and shortening a ton of common patterns of code, very similarly to how the new-ish destructuring syntax did a few years ago.


So, there are example use cases in the proposal [1]. I think it's meant to standardize how we handle conditional inputs or shapes of inputs (some people use if statements, some switch, and others store it all in an object and use keys).

1. https://github.com/tc39/proposal-pattern-matching#motivating...


I would imagine you could do typeof === "string" or instanceof Foo to add JS's limited type support? Elixir is similar in that it's not a typed language but can allow limited types like this.

I personally wish the syntax was similar to overloading functions in other languages, but I doubt that's possible due to the way destructuring & default values were implemented.


I'm not familiar with the JS proposal process. Is this likely to become an official feature now that it's been proposed? If so what's the usual timeline of that look like? I would love to have this feature ASAP in the browser and/or Node.js and am eager to find out when that will be possible.


Right now this says it is a Stage 0 proposal, or "Strawman". It's to see if there is interest in the committee to pursue it. So it's hard to tell the likelihood if it will show up in browsers/Node just yet.

The TC39 process: https://tc39.github.io/process-document/


This is a Stage 0 proposal. Basically it means somebody with a connection to the TC39 (the committee in charge of the JavaScript standard) has thought this was a good enough idea to put together into a proposal.

In terms of chances of standardization, that's a step up from "Hey, I have a great idea", buy there are plenty of Stage 0 proposals that go nowhere because they lose steam.

I think the metric they look for is who is using the Babel plugin for it. If people are doing that, then they it has a better shot of progressing.

Stage 4 is the "this is getting standardized next time" stage


Even Stage 4 isn't granted it will end up on the standard, some proposals have died on Stage 4.


Any example? Since stage 3 is roughly "request for implementers", and Stage 4 requires 2 real world implementations and essentially mean "will be part of the standard in the next wave".

The closest thing I can think of is ES6 modules, because the module loader spec wasn't finished, but the syntax still lived on.

Now, stuff getting kicked down from Stage 3, that has happened.


I was wrong, on my mind I was thinking SIMD has been dropped during stage 4.


Someone will surely do a Babel plugin to use the syntax allowing you to try it before it becomes an official feature.


It's at the bottom of the proposal https://github.com/babel/babel/pull/7633



This should help you: http://2ality.com/2015/11/tc39-process.html

Basically, it's just a proposition for the moment, there's no telling if that's gonna be in the language or not. It's only at stage 2 that it is likely that the feature will be added in the language.


Yeah, I think that's essentially the idea. However the longer part is getting the support native in the browsers. Tools like babel will most likely provide some form of support before the browser does.


I love pattern matching, but find libraries with sugar or unintuitive APIs hard to navigate. I built Rematch[1], which I hope avoids those two issues.

[1] https://github.com/jiaweihli/rematch


Is that really the most pressing problem with Javascript? The lack of one more way of expressing a common pattern? Not, say, the lack of typing or ease of code obfuscation or unintuitive type comparisons the lack of a standard library or ...?


What an ugly syntax. They should follow switch/case syntax for consistency.


Pattern matching doesn't work the same way as switch/case though. Using that syntax would be misleading and confusing.


I think there's an argument to be made that switch statements already do something adjacent to pattern matching. At the least, it's close enough that something like:

  switch(expr) {
    case 'foo':
      break;
   case { foo: bar }:
      break;
  }
Wouldn't strike me as all that strange. It just shifts the semantics from "are you exactly this" to "do you look like this". For non-object primitives (e.g. string or number), I don't think those two things are functionally different.

Granted, it doesn't help make switch any easier to learn, but I _do_ think it makes switch more _rewarding_ to learn. Right now, switch is basically just a restrictive, potentially terser version of an if statement. This would make switch actually useful to learn.

It might even allow us to add some more semantics to switch over time (e.g. constructor matching with something like 'case Number').

(EDIT: thanks to armandososa for teaching me a new thing!)


I'd argue we should strive to move away from the switch syntax where it's not necessary because it's verbose and has curious semantics [1]. As an example, this should work:

  switch(expr) {
    case 'foo':
      console.log("a string")
      break;
    case { foo: bar }:
      console.log("foo is", bar)
      break;
  }
But how would this work?

  switch(expr) {
    case 'foo':
    case { foo: bar }:
      console.log("foo is", bar)
      break;
  }
Moreover, a big selling point of pattern matching is it is an expression. Keep in mind though case points to a statement list, How do we resolve to a value?

With a "return"?

  function f() {
    switch(expr) {
      case 'foo':
        return 4 // This makes f return 
    }
  }

  function g() {
    var x = switch(expr) {
      case 'foo':
        return 4 // But this doesn't. Is this confusing?
    }
  }
The value of the last statement?

  function g() {
    var x = switch(expr) {
      case 'foo':
        4; // This should resolve to 4
        break; // but is the break necessary now? 
    }
  }
IMO switch sytnax is just legacy left behind by C, and not the best one to keep around, especially given the semantics of pattern matching.

[1]: https://en.wikipedia.org/wiki/Duff%27s_device, not sure if this works on JavaScript (I doubt it), but the point is the switch cases essentially work like "goto"s.


You'll need to insert a newline and 2 spaces before a code block to be recognized as such:

    switch(expr) {
       case 'foo': break;
       case { foo: bar }: break; 
    }
https://news.ycombinator.com/formatdoc


Ah, thanks. I should have guessed it would work something like that.


C's switch/case is essentially a special case with a limited set of values (literals) and no destructuring. You can extend it to less trivial patterns and blam pattern matching.


No, switch is not like a special case of match. C's switch is a kind of computed goto and can build irreducible control flow graphs. match is a reducible structured control-flow construct whose power comes from destructuring. match is much, much more like an if-else chain than a switch.


> No, switch is not like a special case of match.

Sure is. Or a degenerate one if you prefer.

> match is a reducible structured control-flow construct whose power comes from destructuring.

Match on sum types with no associated data and you have a switch.

> match is much, much more like an if-else chain than a switch.

Depends on what you're matching.


No it isn't. Again, switch can build irreducible control flow, match cannot. This code

    switch (x) {
    case 0:
        while (i < l) {
            s *= i;
    case 1:
            i += 1;
        }
    }
cannot be rewritten with a simple match. You'd have to reloop the CFG.


Why not? In Haxe (a statically typed language which compiles to js, amongst other targets) pattern matching happens in a switch: https://haxe.org/manual/lf-pattern-matching-structure.html


Let's see an example of your proposed alternative syntax.


Here you are:

    var x = match (response) {
        case { status:200 }: true;
        case { status: 404 }: new NotFoundError;
        case Number: Math.PI;
        case SomeClass: 1;
        case /^http/: "http error";
        default: -1;
    };


A problem with that is that, in JavaScript, case clauses in switch statements fall through to the next clause, whereas they shouldn’t in match statements.

When reading the code, that means you need to hunt for the match or switch statement to know what happens at the end of a case clause.

IMO, if one is willing to correct historical errors, even if they are engrained, Apple’s Swift language makes the best choice here. It requires an explicit fallthrough to fall through to the following case, and goes even further than what you propose by only having a switch keyword (https://developer.apple.com/library/content/documentation/Sw...)


I like it, especially `default` instead of `{}`

The leading curly braces in the linked proposal _are_ a bit odd to see, though the repetition of `case` isn't too fun either.


Braces can be used in many cases, but `case` keyword is used only in `switch` and `match` and is not ambigious. You can see what construct it is from the first word.


I suspect the example in the proposal refers to an object that doesn't match any of the previous options, but is still an object.

For a default I'd expect there to be another pattern.


The default pattern is a variable name.

> [About variable name patterns binding to the name] Eliminates the need for an else/default leg, because assignment to any variable will be sufficient. JS programmers are already used to assigning variables that are then ignored (in functions, in particular), and different people have different tastes in what that should be. _, other, etc, would all be perfectly cromulent alternatives.


I don't like it. This is a false consistency. It looks like a switch but works totally differently (match is semantically more like an if-else chain). The proposal author gives a similar rejection of this syntax under the section "=> for leg bodies" in the proposal.


I think an example would resemble how Swift integrated pattern matching into their regular switch statements, it doesn't require too much creativity to envision.


That's not how it works. You don't have to be Steven Spielberg to dislike some Hollywood movie. How is this fallacies called?


There's a totally different barrier of entry for that than suggesting a syntax. That's not even in the same stratosphere.


There was a suggestion: Make it like switch/case.


I think match cases should be functions, since you have built those anyway.

So instead of match (x) { expr... } you would have something like match(x, [ fn... ]) or match(x) { name: fn, name2: fn2 } where fn is like (objectToMatch) => { expr }


It could look like this:

    var x = match (response) {
        case { status:200 }: true;
        case { status: 404 }: false;
        case Number: 0;
        case SomeClass: 1;
        default: -1;
    };


so make switch first class?

    var x = (switch(true){
        case response === { status: 200 }:
            return true;
        case response === { status: 404 }:
            return false;
        case response === Number:
            return 0;
        case response === SomeClass: 
            return 1;
        default:
            return -1;
    });


> so make switch first class?

No. That would make the actual switch statement a value, rather than make switches expressions. And your version is completely different as you make cases into guards rather than labels or patterns.


This would introduce a weird inconsistency with cases for match and cases for switch regarding fallthrough, though. If it's going to look like cases in a switch statement, it should act like them, for better or worse.


I wonder if they can make it behave like Elixir's pattern matching. Multiple functions with different params, same name.


Elixir's multiple function heads are just syntax sugar for a case statement.


This reads very much like an ideology first, evidence second piece.

How on earth is it bizarre to make neighborhood roads worse to drive on for commuter traffic? And defunding public transport?

You can have a debate about these things, but calling it bizarre betrays the sort of "reasoning" that is dominated by an ideological anti public-anything bias.


I'd like to see how this would handle nested matching in the examples.


are the `=>` actual hidden functions with own scope or are they syntactic sugar? If they are functions they wont have a name in stack traces and add overhead.


are there any 10+ year old languages that are "advancing" as quickly as JS? Ruby? Python? C++?

Why Not?


I love where JS is heading, but perhaps its worth pointing out its a lot easier to rapidly advance a language that historically has been missing huge features.


I'd also argue none are as important as JS. People can choose to not use Ruby, Python or C++, but if you're doing web atm you're pretty much stuck with JavaScript.


You mean are there any other languages trying to quickly catch up to C++'s complexity?


All professional languages reach C++'s complexity, which probably is not as complex as PL/I or Algol 68W were for their time.

Python is my favourite example to pick up on this.

Target at beginners and deemed as simple, yet I doubt anyone is able to know Python semantics since version 1.0 and by looking at a random codebase is able to state what is the minimum Python version required to run the code without errors.

Also I very much doubt anyone knows Python's library cover to cover.

Languages get complex because real world has complex needs.

Even Go, the new poster child of simplicity, now has quite a few warts, because not everyone doing software like Google.


Fair enough, and Python is a good example, but I'm guessing Scheme and Smalltalk managed to stay relatively simple over time, although I don't know how much of that would be chalked up to lack of mainstream support.


C++11 and 17 both added a lot of big new features, and there's more planned for C++20. For the language itself, I think it's completely comparable.


I should note, this isn't my proposal. Just found it interesting enough to share


Thanks for sharing.


For those looking to digest more information on pattern matching in the JS world, there has been an open proposal for TypeScript to support pattern matching of some flavor.

https://github.com/Microsoft/TypeScript/issues/165


Out of the major, popular languages today, Javascript is easily the worst.


Flame bait on HN is discouraged and could eventually get you banned.


Don’t disagree, but what an odd place to state it. It's almost like you want to argue with js programmers.


Stop using (nested) ternary operator's ! Use if-statements!

  if(val==1) var res = 1;
  if(val==2) var res = 2;


Except ternary expressions have the huge advantage that they allow assigning to const. Eg:

    const var =
      val == 1 ? 1 :
      va1 == 2 ? 2 :
      null;


It's hard to argue about not using const, but can you remember any time where const actually prevented a bug !?


There's real value in the fact that ternary operators form expressions, which if statements don't.


Could you elaborate ? I think the only difference is readability, where I favor if-statements over expressions.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: