Extended dot notation (pick notation) proposal

Bob Myers rtm at gol.com
Sat Sep 24 20:47:03 UTC 2016


I'd like to reframe the discussion a bit, starting with a definition of
picking.

1. "Picking" means to create a new object ("pick target") containing a
subset of specified properties and their values drawn from another object
(or possibly several other objects) ("pick source").

2. "Renaming" means that a property is given a different name in the target
than the source.

3. "Defaults" means that a property missing in the source is given some
specified value.

4. "Deep picking" means that a property to be put on the target may be
drawn from within a nested object on the source.

So all of the following things are picking:

```
{p1: o.p1, p2: o.p2}

const {p1, p2} = o;
{p1, p2}

(({p1, p2}) => ({p1, p2}))(o)

_.pick(o, 'p1', 'p2')
```

I will take it as axiomatic that most JS programs contain a reasonable
amount of picking. The question here is whether to add core language
features to support picking. The normal criteria for new language features
apply. Is it something that cannot be done in userland code, or is it mere
syntactic sugar? In the latter case, how sweet is the sugar? Depending on
the specifics of the proposed syntax, to what extent does it improve
readability, compactness, and program correctness? Do these considerations
balance against the "cognitive burden"? Are there optimization
considerations? Is it typeable? Are there parseability concerns?

Picking **is** merely syntactic sugar. So we need to examine the current
picking approaches given above and reason about them in the context of
these criteria. We'll start with `{p1: o.p1, p2: o.p2}`. It's unsatisfying
that `o`, `p1`, and `p2` are all repeated twice in this construct. Each
such repetition has a potential for error. Because of the duplication, the
overall construct seems longer than ideal. It supports renaming and deep
picking, in the form of `{newp1: o.p1, p2: o.x.p2}`. But it does not
support defaults, unless one wants to write `{p1: o.p1, p2: 'p2' in o ?
o.p2 : "default"}`. In all cases it is perfectly typeable.

I often see the second alternative of `const {p1, p2} = o; target = {p1,
p2};`. But this is also intellectually unsatisfying, because again the `p1`
and `p2` must be repeated, and the local scope is polluted with unnecessary
new variables. However, it does support defaults by virtue of defaults in
deconstructing assignments.

The third alternative of `(({p1, p2}) => ({p1, p2}))(o)` seems unduly
awkward.

A library routine such as `_.pick` is the solution many people point to,
but it also seems unsatisfying. It is not easily extensible to support
defaults or renaming. It is also hard to implement type safety.

So at the end of the day it boils down to a subjective judgment about the
tradeoff between remedying these deficiencies and the cognitive burden and
complexity of a new language feature that attempts to address them. Of
course, this tradeoff depends on the specifics of some proposed new syntax,
and to what extent it is "obvious", or "natural", or leverages existing
syntax features in an intuitive way, or in general has a smaller perceptual
footprint.

If your judgment is that any incremental cognitive burden whatever could
not possibly justify any new syntax, because the deficiencies are just not
that severe, then you are not in favor of this proposal and that's fine.
We'll call this position A, the "just say no" position. The converse is
position B, the "I want picking in the language" position.

If you take position B, and thus are willing to consider new syntax, but
want to absolutely minimize the footprint, acknowledging that all the
design goals might not be met, then we can adopt @Bergi's proposal to
simply extend existing shorthand property notation, as follows:

```
{o.p1, o.p2}
```

We will call this position B1, "qualified shorthand properties". It's
definitely an improvement! But it still requires repeating `o`, and also
doesn't give us defaults or renaming.

But if we want a "real" picking construct, then assuming it's clean enough
to overcome the cognitive burden-vs-benefit hurdle, almost by definition it
must involve (a) an object to pick from; (b) a specification for which
properties to pick (the "picker"); and (c) a mechanism to indicate that
picking is to occur.

One obvious mechanism to indicate that picking is to occur is to introduce
a picking operator. We will call this position B2, the "I want a picking
operator" position.

In that case consider the following:

1. We already have the dot notation, which takes a single property from an
object and returns its value as a scalar. The dot notation currently only
takes an identifier--the key--on its right hand side. Anything else is a
syntax error. In other words, the dot `.` can already be considered a
particular kind of pick operator, limited to picking scalar values.

2. We already have the deconstructing pattern syntax, which is a way of
specifying a set of properties (including computed properties) to extract
from an object, along with ways to rename and assign default values to
those properties. In other words, it is a kind of "picker". But beyond its
use in parameter deconstruction, it is currently used only in
deconstructing assignment. The extracted values are exclusively used as
values to assign to *variables*.

That is what is behind the proposal to use the existing dot as the pick
operator. That is why this proposal is called "extended dot notation". And
we use an *AssignmentPattern* as a "picker". We combine these two familiar
notions into the syntax `o.{p1, p2}`. Voila, we have picking, defaults,
deep picking, and renaming by virtue of those features already being in the
deconstructing pattern syntax.

We will call this position B2a, "I want to use the dot for the picking
operator".

The gist is:

> You can put a deconstructing pattern after a dot, and it will create a
new object with the deconstructed names/values as properties.

If the dot is considered too sacred to repurpose, or if we want something
that is more visible than a dot, then we could substitute the token `pick`.
We will call this position B2b, "I want to use `pick` for the picking
operator". The syntax would be

```
o pick {p1, p2}
```

As far as I can see, this is entirely backward compatible and parseable.

However, there is another possible mechanism to indicate that picking is to
occur. That is to place new syntax *inside* the existing `{}` object
literal delimiters. The conceptual notion is something along the lines of
`{ PICK-P1-AND-P2-FROM-O }`. In other words, optimize the `{p1: o.p1, p2:
o.p2}` syntax. The proposed way to do this is to put something that looks
exactly like a deconstructing assignment inside the curly brackets:

```
{ {p1, p2} = o }
```

These nested curly brackets may put off some folks. However, this approach
does have the advantage of exactly re-using existing deconstructing
assignment syntax. The gist is:

> A deconstructing assignment can be put inside curly brackets, and the
resulting names and values are put into the object as properties

This particular solution has the advantage that you can deconstruct from
multiple objects into a new object, as in

```
{ {p1, p2} = o, {q1, q2} = o2 }
```

We will call this position B3, "I want to pick by putting a deconstructing
assignment inside curlies".

For your convenience, here is a summary of the positions:

A: just say no
B: I want picking in the language
B1: I want qualified shorthand properties
B2: I want a picking operator
*B2a, *I want to use the dot for the picking operator
B2b: I want to use `pick` for the picking operator
B3: I want to pick by putting a deconstructing assignment inside curlies

By the way, sorry for getting drawn into the "reduced cognitive load"
discussion.
Yes, this proposal does involve additional cognitive load.
The right question is how to minimize it, and to what extent it is
justified.

--
Bob

On Sat, Sep 24, 2016 at 4:27 AM, Jason Orendorff <jason.orendorff at gmail.com>
wrote:

> On Thu, Sep 22, 2016 at 1:14 PM, Bob Myers <rtm at gol.com> wrote:
>
>> Sorry for using ALL CAPS. I will not do that any more. You're right: this
>> proposal DOES (oops, I meant *does*) increase the size of the spec. Is that
>> the new criteria, that no proposal may increase the size of the spec?
>>
>
> This conversation has been kind of strange for me.
>
> JO: new cognitive burden has to be justified
> BM: this isn't new cognitive burden, it's removing cognitive burden
> JO: that makes no sense
> BM: are you saying we need to freeze js forever?
>
> ...Well, no. Obviously.
>
> But new cognitive burden has to be justified.
>
> TC39 didn't accept array comprehensions. They were generally well-liked,
> but they didn't introduce any new capabilities and it was decided they
> didn't pull their weight. That's the bar any new proposal has to get over.
>
> Then we might as well freeze JS right now.
>>
>> The "extra words" you refer to are nothing more this:
>>
>> > You can also put deconstructing syntax following a dot after an object.
>> This will result in a new object with properties determined by the
>> deconstructor.
>>
>
> I think your gist is more a serious attempt at a reasonable explanation.
> Don't trivialize it -- people who *don't* get the memo will have to search
> StackOverflow for `.{`.
>
> -j
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20160925/c262c3ce/attachment.html>


More information about the es-discuss mailing list