Extensible destructuring proposal
Andreas Rossberg
rossberg at google.com
Wed Aug 5 08:18:36 UTC 2015
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/ed3e8cf5/attachment.html>
More information about the es-discuss
mailing list