On Wed, Aug 5, 2015, 04:29 Isiah Meadows <[email protected]> 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 <[email protected]> wrote: > >> On 5 August 2015 at 09:27, Isiah Meadows <[email protected]> wrote: >> >>> Damnit...forgot to fix the subject. >>> >>> On Wed, Aug 5, 2015 at 3:20 AM, Isiah Meadows <[email protected]> >>> 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 <[email protected]> >>>> > To: "Samuel Hapák" <[email protected]> >>>> > Cc: es-discuss <[email protected]> >>>> > Date: Tue, 4 Aug 2015 14:26:45 +0200 >>>> > Subject: Re: Extensible destructuring proposal >>>> > On 31 July 2015 at 20:09, Samuel Hapák <[email protected]> >>>> 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 >>> >>
_______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

