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).
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.
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.
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.
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.
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.
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).
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.
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 */;
...
}
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`?
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)
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.
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.
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.
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.
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.
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.
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)
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.
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]
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.
The ternary operator ?: is an expression, but for expressions containing statement blocks, you have function() {} and () => {}, including the immediately invoked function expression, (function() {}())
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:
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.
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.
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.
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:
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).
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.
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.
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.
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.
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.
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 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.
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.
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 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.
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.
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"...
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.
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.
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.
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
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.
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;
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.
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.
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.
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.
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).
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.
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
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.
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.
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 ...?
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.
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.
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...)
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.
> [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.
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 }
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.
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.
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.
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.
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.
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.
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.