Wait...this got me thinking... The proposal itself doesn't bring along a lot of merits, but it seems like it could be a great stepping stone to a limited pattern matching syntax. This would probably be a little more justifiable IMHO than merely a custom destructuring syntax. Maybe something like this:
```js Type[Symbol.pattern] = (obj) => { return [obj.a, obj.b]; } const Other = { [Symbol.pattern]: obj => obj, } class None { static [Symbol.pattern](obj) { return obj } } // Pattern matching, signaled by `in` here switch (object) in { case Type([a, b]): return a + b case Other({a, b}): return a * b case None: return undefined // Special case, no identifier initialized } // Extensible destructuring, easy to implement with the pattern // matching let Type([a, b]) = object let Other({a, b}) = object ``` In the destructuring phase for both, I was thinking about the following semantics to assert the type, based on `typeof` and the prototype. This will help engines in optimizing this as well as some type safety for all of us. ```js function _checkProto(object, Type) { // Note: Type[Symbol.pattern] must be callable if (typeof Type[Symbol.pattern] !== 'function') throw new TypeError() if (typeof Type === 'function') { if (type === Array) { return Array.isArray(object) } else { return object instanceof Type } } else { return Object.prototype.isPrototypeOf.call(Type, object) } } function isInstance(object, Type) { switch (typeof object) { case 'object': return obj != null && _checkProto(object, Type) case 'function': return Type === Function case 'boolean': return Type === Boolean case 'number': return Type === Number case 'string': return Type === String case 'symbol': return Type === Symbol case 'undefined': return false } } ``` Finally, get the result and do a basic variable pattern assignment, LHS being the operand, and RHS calling `Type[Symbol.pattern]`. The `switch` statement example would (roughly) desugar to the following: ```js switch (true) { case isInstance(object, Type): let [a, b] = Type[Symbol.pattern](object) return a + b case isInstance(object, Other): let {a, b} = Other[Symbol.pattern](object) return a * b case isInstance(object, None): return undefined } ``` The destructuring examples would (roughly) desugar to this: ```js if (!isInstance(object, Type)) throw new TypeError() let [a, b] = Type[Symbol.pattern](object) if (!isInstance(object, Other)) throw new TypeError() let {a, b} = Other[Symbol.pattern](object) ``` The type assertions will help engines in optimizing this, and it'll also make this safer. It also just makes sense for pattern matching. As a side effect, you can get the value without destructuring (i.e. the literal result of `Symbol.pattern`) via this: ```js let Map(m) = someMap let m = Map[Symbol.pattern](someMap) ``` I, myself, came up with a Scala-inspired case class concept <https://gist.github.com/impinball/add0b0645ce74214f5aa> based on this, and used it to make try-catch handling a little more Promise-like, which I know quite a few TypeScript users would eat up in a hurry <https://github.com/Microsoft/TypeScript/issues/186> (particularly the sum type implementation). I also created a little toy Option/Maybe implementation <https://gist.github.com/impinball/4833dc420b60ad0aca73> using pattern matching to ease `null`/`undefined` handling. And also, check out my gist <https://gist.github.com/impinball/62ac17d8fa9a20b4d73d> for a proposed `Symbol.pattern` prolyfill for the primitive types. That would allow for things like this: ```js switch (list) in { case Array([a, b, c]): return doSomething(a, b, c) case Map({a, b, c}): return doSomethingElse(a, b, c) default: return addressBadType(list) } ``` > ---------- Forwarded message ---------- > From: Andreas Rossberg <rossb...@google.com> > To: "Samuel Hapák" <samuel.ha...@vacuumapps.com> > Cc: es-discuss <es-discuss@mozilla.org> > Date: Tue, 4 Aug 2015 14:26:45 +0200 > Subject: Re: Extensible destructuring proposal > On 31 July 2015 at 20:09, Samuel Hapák <samuel.ha...@vacuumapps.com> wrote: >> >> So, do you still have objections against this proposal? Could we summarize them? >> >> @Andreas, do you still think that there is category error involved? > > > If you want to overload existing object pattern syntax, then yes, definitely. I strongly disagree with that. It breaks regularity and substitutability (you cannot pass a map to where a generic object is expected). > > As for a more Scala-like variant with distinguished syntax, I'm fine with that semantically. But I still don't buy the specific motivation with maps. Can you give a practical example where you'd want to create a map (immutable or not) for something that is just a record of statically known shape? And explain _why_ you need to do that? Surely it can't be immutability, since objects can be frozen, too. > > To be clear, I wouldn't reject such a feature outright. In fact, in a language with abstract data types, "views", as they are sometimes called in literature, can be valuable. But they are also notorious for a high complexity cost vs minor convenience they typically provide. In particular, it is no longer to write > > let {a: x, b: y} = unMap(map) > > as you can today, then it would be to move the transformation to the pattern: > > let Map({a: x, b: y}) = map > > So I encourage you to come up with more convincing examples in the JavaScript context. Short of that, I'd argue that the complexity is not justified, at least for the time being. > > /Andreas -- Isiah Meadows
_______________________________________________ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss