Extensible destructuring proposal

Isiah Meadows isiahmeadows at gmail.com
Wed Aug 5 08:30:48 UTC 2015


On Wed, Aug 5, 2015, 04:29 Isiah Meadows <impinball at gmail.com> wrote:

> Good point. I did make most of the natives identity functions returning
> the object itself for most cases with the prolyfill, which engines should
> easily detect at run time and inline into nothingness. But I do see your
> point. As proposed, I know it's still incomplete in its core functionality
> (pattern matching on array literals would be a bit more interesting), but
> the other core problem is this: JavaScript isn't statically compiled.
> Nearly every language with patten matching is. It's much easier to optimize
> this into something decently performant in a static context (TypeScript as
> an example).
>
> On Wed, Aug 5, 2015, 04:19 Andreas Rossberg <rossberg at google.com> wrote:
>
>> On 5 August 2015 at 09:27, Isiah Meadows <impinball at gmail.com> wrote:
>>
>>> Damnit...forgot to fix the subject.
>>>
>>> On Wed, Aug 5, 2015 at 3:20 AM, Isiah Meadows <impinball at gmail.com>
>>> wrote:
>>>
>>>> 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:
>>>>
>>>
>> I intentionally did not bring up pattern matching. That indeed is what
>> views are usually wanted for. But then you need to be much more careful in
>> designing a mechanism that avoids re-transforming the scrutinee for every
>> tested case! Because that would be very costly. For that reason, I fear
>> that the feature as proposed would interact badly with any future pattern
>> matching mechanism, in the sense that it would encourage very costly usage
>> that cannot be optimised.
>>
>> /Andreas
>>
>>
>>
>>>
>>>> ```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 <rossberg at google.com>
>>>> > To: "Samuel Hapák" <samuel.hapak at vacuumapps.com>
>>>> > Cc: es-discuss <es-discuss at 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.hapak at 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
>>>>
>>>
>>>
>>>
>>> --
>>> Isiah Meadows
>>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20150805/5d175e12/attachment-0001.html>


More information about the es-discuss mailing list