Edit: Note: the bulleted list of differences may be out of date at this point.
I was working on my own personal strawman about the same time, and I didn't realize that one of you all would end up beating me to it.
I did, shortly after noticing this, make a few modifications (allow blocks and modify how I define bindings), but I am curious how you all feel about mine compared to this.
Note: Please comment here, not on my gist. I'm not going to get notified of comments there, because GitHub seems to have missed a very nice bit of UX in that area. 😦
Here's a quick summary of some of the differences:
case instead of match. There is a cover grammar (included in the gist) to differentiate from case clauses in switch statements, but it's fairly trivial, and I'm aware of no other potential ambiguities.is for all native brand checks, including typeof and instanceof. (A little Ruby-like.)Map, Set, and libraries like Lodash/Immutable in mind.binding -> pattern, I use binding = pattern. (It's not ambiguous.)@@test and @@unapply.And yes, it's a bit meatier, but I've also looked into some of the optional extensions:
@@test and potentially @@unapply for advanced cases. (I drew more inspiration from Scala than F# in it, though. The similarities are just coincidental)if predicates are already included, since I thought of them more as a primitive than an add-on.is clauses (i.e. brand checking), as opposed to something like @@matches. It's destructuring or === everywhere else.I did at one point have a multi-pattern syntax (comma-separated patterns), but I removed it because that's one thing I'm not quite certain how it should fit.
Overall I like it quite a bit - the little bit of prefix syntax you use nicely separates the distinct basic cases.
I find a lot of value in avoiding using terminology that's used with switch - with case, I can see people being confused between the two, whereas with match, "always use match and never switch" becomes really easy advice to give.
I did consider with, but that basically throws sloppy mode use out the
window. (I recall past TC39 discussion on whether sloppy mode use should be
accommodated with new features.)
I also thought of match myself previously, but I was hesitant to add a
NLTH rule before a brace, and I was hesitant to drop the parentheses
(aesthetic reasons).
On Tue, Jul 4, 2017, 14:52 Jordan Harband notifications@github.com wrote:
I find a lot of value in avoiding using terminology that's used with
switch - with case, I can see people being confused between the two,
whereas with match, "always use match and never switch" becomes really
easy advice to give.—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
https://github.com/tc39/proposal-pattern-matching/issues/17#issuecomment-312934674,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AERrBAuP1JMFT5xchC7KXIKVkE4vcQY6ks5sKon7gaJpZM4ONeb0
.
@isiahmeadows I like much of that proposal. I'm actually pretty encouraged that it seems fairly similar with the exception of some syntax extensions (?, =) and the runtime aspect. Especially with https://github.com/tc39/proposal-pattern-matching/issues/11#issuecomment-313261317 which proposes as which is is in reverse I think.
I think the same optimization strategy used by clojure should apply to this proposal, but I'm not entirely sure - can you expand on what the requirements are here?
Also, maybe we could open separate issues for the various other missing pieces like a non-binding placeholder like ? and strict-equality short-cut =?
I'm a big fan of this incarnation of the idea. I was about to suggest something like @@unapply, but this incarnation already has it worked out.
@isiahmeadows Where did the idea of else comes from too?! haha I'm curious! As I replied here, this seems to be useless since (x = x) will match as well, as any programming language I know. What would be the benefits?
The whole idea of else is just to catch everything else without a
fallthrough.
(re: what I addressed) I'll list that once I get to a computer, where it's
a bit more convenient to go through GH issues.
On Thu, Jul 6, 2017, 03:18 Wagner Leonardi notifications@github.com wrote:
@isiahmeadows https://github.com/isiahmeadows Where did the idea of else
comes from too?! haha I'm curious! As I replied here
https://github.com/tc39/proposal-pattern-matching/issues/25#issuecomment-313307833,
this seems to be useless since (x = x) will match as well, as any
programming language I know. What would be the benefits?—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/tc39/proposal-pattern-matching/issues/17#issuecomment-313316526,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AERrBPR0-2E6h3hTOc0nyVARhA9t48-5ks5sLIpVgaJpZM4ONeb0
.
@bterlson
Most of the similarities is that Clojure's core.match, like both my proposal and the one here, matches structurally by default (Clojure's maps/vectors ↔ JS's objects/iterables), and it has extensions for 1. custom matchers (like my @@test/@@unapply and this one's @@matches) and 2. protocol-based matching (like my built-in {Function,String}.prototype[@@test]). Additionally, Clojure doesn't have language-level sum types like most other FP languages due to its Lisp heritage, and their idiomatic replacement (symbols) isn't far off of what we use in JS land (strings). That's why I referred to it in my proposal as something potentially informative.
Here's some explanation of some of my less obvious choices in my proposal:
is) to avoid most of the messiness and slowness you'd get out of it.= to allow checking named values in patterns, like in [= INST_INVALIDATE] => invalidate(), so the identifiers would be evaluated instead of bound. It's to avoid an ambiguity with [INST_INVALIDATE] => invalidate(), which would bindINST_INVALIDATE` to the sole item in the pattern.undefined/null without issue. For similar reasons, I provided prop: ? so you can check for properties without using them, and [?] as a more verbose way of effectively specifying an elision. Those could easily be removed if the use case is rare enough.Regarding how I've addressed existing issues (including some of the ones filed since I filed this):
else is more of a specific exception for a common case: you want a catch-all case, but the value is irrelevant.?.Function.p[@@test], String.p[@@test], and Array[@@test] (just added) as built-ins. I also have {Map,Set,WeakMap,WeakSet}[@@unapply] to simplify destructuring. I didn't include everything, but I did include a few critical ones.is Type of ... and the @@unapply extension point.= when not directly binding the value.[[GetProperty]] but iterate prototypes. I forgot to clarify this in the semantics portion of the gist. Optionally, you could also use in + [[Get]], which would be easier to transpile, and I'm personally split on which is better (engines could optimize them both easily).@isiahmeadows thanks for this detail, very interesting!
Per https://github.com/tc39/proposal-pattern-matching/issues/11#issuecomment-317136273
If a user wants to use instanceof, they should be able to supply it, but it shouldn't ever be the default because it doesn't work cross-realm.
https://github.com/jasnell/proposal-istypes imo is the best way to do it; instance checking imo shouldn't be built in to matching unless it uses something like that proposal.
@ljharb BTW, I use Array[Symbol.test] === Array.isArray in the proposal over checking instanceof Array, so I am open to that (and would optimally prefer it). I use instanceof as a fallback in case a type doesn't define something better, but I'd actually prefer using something that works cross-realm for native types.
(The only reason I didn't is because it didn't exist in the current spec.)
There exists a way to do cross-realm brand checking in every builtin type except the Error family (until the stacks proposal hits). I have a number of is-foo packages on npm as exemplars of this method. Regardless, there's no reason the default Symbol.test method can't just be a straight brand check - the polyfill is a separate question.
Oh okay. (I'll note that your is-foo stuff relies on things that could be easily faked, and thus isn't safe for untrusted data.) But yes, my intent for is Array, is Map, and friends was just that of a brand check. I'll edit my proposal accordingly.
Also, FWIW, is Foo always calls Foo[Symbol.test](value). That was basically just a way to abstract the brand check in a way that's easily customized and that allows me to merge typeof with instanceof and other brand checks.
@isiahmeadows as long as my code runs first, it can't be faked - if you find any instance of that, please file an issue. Code that doesn't run first can't have many guarantees, of course.
Okay. Pretty much the only thing that could be faked at runtime is your frequent use of Function.prototype.call, but that's done before any object accesses first with Object.prototype.toString.call, and code from a different realm/context can't overwrite that without permission. (I'm not really that concerned.)
Hey y'all! #65 has gotten merged, and a lot of issues have become irrelevant or significantly changed in context. Because of the magnitude of changes and subtle differences in things that seem similar, we've decided to just nuke all existing issues so we can start fresh. Thank you so much for the contributions and discussions and feel free to create new issues if something seems to still be relevant, and link to the original, related issue so we can have a paper trail (but have the benefit of that clean slate anyway).
Most helpful comment
I find a lot of value in avoiding using terminology that's used with
switch- withcase, I can see people being confused between the two, whereas withmatch, "always use match and never switch" becomes really easy advice to give.