es-discuss Digest, Vol 102, Issue 14
Isiah Meadows
impinball at gmail.com
Wed Aug 5 07:20:02 UTC 2015
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 <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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20150805/56d3760c/attachment.html>
More information about the es-discuss
mailing list