Extended dot notation (pick notation) proposal

# Bob Myers (8 years ago)

Extended dot notation is a proposal for a new syntax for picking properties into objects. The canonical example is

let o1 = { a: 1, b: 2};
let o2 = o1.{a};         // {a: 1}

You may think of this as deconstructing objects into objects, rather than variables.

Syntactically, the extended dot notation is the dot followed by something in curly braces. This avoids eating up another precious special symbol, yet is unambiguous since at present only an identifier can follow a dot.

I've put together a short introduction at rtm/js-pick-notation/blob/master/docs/intro.md. This repository also contains a full spec at rtm/js-pick-notation/blob/master/js-pick-notation.md. The spec includes a section on motivation, which in a nutshell is expressiveness and brevity. The repo also features a proof-of-concept implementation using sweet.js.

A variant of the proposal uses # instead of ., the pros and cons of which are discussed in the spec.

I won't repeat details from the introduction here, but there are more features of interest, including picking into arrays, pickers as functions, etc.

I'm looking for good feedback as well as a champion for this feature.

-- Bob Myers

# Isiah Meadows (8 years ago)
  1. Take a look at esdiscuss.org/topic/pick-operator.
  2. Do keep in mind syntax additions tend to have a steep usefulness requirement in practice (e.g. Async functions) to make it into the language. And most definitely check out esdiscuss.org/topic/the-tragedy-of-the-common-lisp-or-why-large-languages-explode-was-revive-let-blocks.

Not saying this is a bad thing to add, but I'm skeptical on how likely it is to get accepted. Isiah Meadows me at isiahmeadows.com

# Bob Myers (8 years ago)

Thanks. Yes, I should have mentioned the earlier proposal, which I've now marked as being superceded.

I'm aware the bar is high for syntax additions, as it should be. Some possible justifications include:

  1. The need for the picking feature is demonstrated by the _.pick utility provided by toolbelts such as Underscore. It is also the topic of frequent questions on Q&A sites like SO.

  2. The proposed syntax changes are non-intrusive and would have little to no impact on the rest of the language AFAICT.

  3. The proposal subsumes other proposals for Array#last etc. with the syntax a.(-1), and proposals for "null propagation" with the syntax a.b! etc. Two birds with one stone.

Bob Myers

# Bob Myers (8 years ago)

Here's another StackOverflow question stackoverflow.com/questions/39299046/destructuring-a-subset-of-an-object-directly-into-a-new-object

essentially asking for pick notation. This same question pops up regularly. It's the real world talking to us telling us what they need. In this particular case, the solution with extended dot notation would just be

var goodKeys = this.state.{toDate, fromDate, location, flavor};

Perhaps I have not posted the minimal version of my proposal for this pick notation rtm/js-pick-notation/blob/master/minimal/spec.md, also

known as extended dot notation. This version sacrifices some features in favor of complete compatibility with existing destructuring syntax following the dot.

There is another good reason these days for thinking about something about this: the advent of typed dialects of JS. We already know that a simple lodash-type version of pick, such as

function pick(o, keys) {
  return Object.assign({}, ...keys.map(key => ({[key]: o[key]})));
}

const o = {a:1, b: 2};
const o2 = pick(o, ['a']);

cannot handle renaming or defaults or deep picking, but in addition it cannot also not be typed properly. For instance, lodash.d.ts defines pick as:

pick<TResult extends {}, T extends {}>(
  object: T,
  ...predicate: (StringRepresentable|StringRepresentable[])[]
): TResult;

with no type safety whatsoever.

In contrast, with pick notation

const o2 = o.{a};

o2 can be precisely typed by the compiler as an object containing the property a, or a type compatible or assignable to that type.

-- Bob

# Isiah Meadows (8 years ago)

TypeScript has a fair number of proposals aiming to address this (things like difference types, partial types, etc.), but in general, I find it just as easy to do it this way (which is easily checked):

const {toDate, fromDate, location, flavor} = this.state;
const goodKeys = {toDate, fromDate, location, flavor};
# Kris Siegel (8 years ago)

Hmm I gotta say I must have re-read that minimally extended dot notation proposal a few times and I just find the syntax confusing. I do like the idea of building a way of taking a portion of an object out of one object and into another but I don't think we need to provide additional syntax rules to handle this. Why couldn't this be an Object.pick() addition where it works similar to the underscore implementation? It could even be expanded to handle deep nesting (I'm actually adding this to my msngr.js library in the next release as grabbing a subset of an object is crazy useful and I need to do it far too frequently).

Isiah's workaround works but has the unfortunate side affect of copying values / increasing reference counts to objects. I'd love to see a built in solution.

# Isiah Meadows (8 years ago)

Isiah's workaround works but has the unfortunate side affect of copying values / increasing reference counts to objects. I'd love to see a built in solution.

You'd have to do that anyways, even if it's entirely internal. How do you think you would do it outside of copying the values?

# Bob Myers (8 years ago)

Here is a little decision tree of the issues.

[image: Inline image 1]

# Isiah Meadows (8 years ago)

I'm just saying those references still exist, and ignoring them will lead you into more problems than this would solve. First, my above workaround would likely be the desugaring in V8. You could optimize it by creating the actual object and assigning the properties as they come from the object, but that's also a valid optimization for engines to make today with my workaround. Also, most other optimizations are equally permissible with both syntax and my equivalent workaround.

And this isn't C: unless you're using those references, JS engines will clean them up automatically, anyways, because of their garbage collection. My workaround produces 0 more required object references than what the engine needs.

As you can see, I'm generally against this.

# Isiah Meadows (8 years ago)

I'll note that the type checking part will eventually get fixed in TypeScript, at least.

Microsoft/TypeScript#1295

Oh, and I've already suggested another, more specific, pick syntax (that operation is only a shorthand for x => x.property) ,but it ultimately got

rejected since no one could really agree to anything different other than the current state was workable, but not optimal. You may appreciate reading the entire conversation, as it's pretty informative and is related to this.

Here's the specific comment proposing that idea: tc39/proposal-bind-operator#24

Here's the issue where most the related discussion took place: tc39/proposal-bind-operator#26

# Kris Siegel (8 years ago)

I'm just saying those references still exist, and ignoring them will lead

you into more problems than this would solve.

Not suggesting anything of the sort only that your workaround adds additional variables with possible object references and a more native implementation could work around that. Nothing more.

And this isn't C: unless you're using those references, JS engines will

clean them up automatically, anyways, because of their garbage collection. My workaround produces 0 more required object references than what the engine needs.

Hmm this is interesting. It's been a while since I last memory profiled a web application but the last time if I kept any references to, say, DOM elements in any variables even if I was no longer using them they would still be eating up the memory causing memory leaks. Are you saying this is no longer the case? You don't need to necessarily get rid of all references to, say, a DOM element before the garbage collector will sweep it up?

I'm more curious than anything else; it appears my understanding here might be wrong so I want to dig into it more.

Oh, and I've already suggested another, more specific, pick syntax

Good read. Interesting. Honestly I'm more of a fan of introducing functions if possible to handle much of this instead of new syntax though I don't know if that is necessarily supported by others and that proposal goes a bit before what I was originally thinking.

# Isiah Meadows (8 years ago)

On Wed, Sep 7, 2016, 04:10 Kris Siegel <krissiegel at gmail.com> wrote:

I'm just saying those references still exist, and ignoring them will lead you into more problems than this would solve.

Not suggesting anything of the sort only that your workaround adds additional variables with possible object references and a more native implementation could work around that. Nothing more.

And this isn't C: unless you're using those references, JS engines will clean them up automatically, anyways, because of their garbage collection. My workaround produces 0 more required object references than what the engine needs.

Hmm this is interesting. It's been a while since I last memory profiled a web application but the last time if I kept any references to, say, DOM elements in any variables even if I was no longer using them they would still be eating up the memory causing memory leaks. Are you saying this is no longer the case? You don't need to necessarily get rid of all references to, say, a DOM element before the garbage collector will sweep it up?

If the DOM element is not live and either is not referenced, or referenced only by things that are marked for collection, the GC will mark it (assuming they are only referenced by it). Then after a while, it's swept away with the rest of the garbage.

(Technically, this is an oversimplification, but it's a complex topic. Also, this doesn't apply to really old IE, which uses reference counting instead.)

I'm more curious than anything else; it appears my understanding here might be wrong so I want to dig into it more.

You do, but extra syntax for pick makes 0 difference in this area. My point is that the new references in between are created and retained regardless of how you create them.

Oh, and I've already suggested another, more specific, pick syntax

Good read. Interesting. Honestly I'm more of a fan of introducing functions if possible to handle much of this instead of new syntax though I don't know if that is necessarily supported by others and that proposal goes a bit before what I was originally thinking.

I agree, too. And for individual properties, it is helpful, but this language isn't quite as functional as people want to think. Also, it's not like the alternatives are much larger: x => x.property (and for the

original proposal's shorthand: (...xs) => console.log(...xs)).

# Bob Myers (8 years ago)

I just find the syntax confusing

It combines two syntaxes we know and love: (1) dot notation o.b and (2) deconstructing notation {a} = o, into o.{a}.

Maybe some have problems with the dot in a.{o} being too inconspicuous. At the cost of using up a precious special character, we could go with o # {a} instead. Actually I think o pick {a} is syntactically unambiguous.

I do like the idea of building a way of taking a portion of an object out of one object and into another

Good, many people seem to agree.

Why couldn't this be an Object.pick()

Personally, passing around properties as strings grates on me. Then there's the issue of typability, although Isiah has pointed out a useful TypeScript proposal that does deal with that. Within the _.pick paradigm, it's hard to envision a concise syntax for defaults and renaming, which the deconstructing-like syntax gives us for free.

It could even be expanded to handle deep nesting

Just curious, what sort of interface to your _.pick-like thing do you have in mind? Will it support defaults and renaming?

By the way, I was looking at Elm and found it supports one advanced feature of extended dot notation, which is the unary dot as function, as in arr.map(.p).

Bob

# Bob Myers (8 years ago)

Since some people seem to find the proposed pick notation o.{p1, p2}, (to create an object composed of the p1 and p2 properties from o) confusing, I am making an alternative proposal which hews more closely to existing deconstructing assignment.

The definition of the new syntax is:

If a deconstructing assignment is wrapped in curly brackets {}, the result is a new object where the deconstructed names and values become property names and values.

The above case would be written as:

{ {p1, p2} = o }

and would result in

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

AFAICT the above syntax should be uniquely parseable. The advantage of this syntax is the minimal cognitive footprint: if you know how to deconstruct an object into variables using deconstructing assignment, then you can deconstruct an object into properties on a new object with nothing more than an extra set of surrounding curlies.

I will be adding this alternative syntax to the repo here rtm/js-pick-notation and here rtm/js-pick-notation/blob/master/minimal/spec.md in

the near future.

Bob

# Bob Myers (8 years ago)

People in the real world continue to wonder why they can't pick/destructure from objects into objects, instead of just variables.

stackoverflow.com/questions/39602360/es6-destructuring-reassignment-of-object?noredirect=1#39602360

This is actually the second such question on SO in the last week.

Bob

# even stensberg (8 years ago)

I think that for now, personally, this should be in userland. Currently, making your own functions provides much more flexibility than the need of the operator in general. What I mean by that, is that in traditional ways, you'd have the need to pick your values of either an array or object by yourself.

In the pragmatic approach, you'd need to write an utility anyway, as it is not common to fetch only one value from the destruct. You'd pick what you need. Similarly, that's how generators and functions are about too.

As for the proposal, it might seem like a good thing to have this feature, but eventually, in big scale and even lesser projects, you'd have a tendency and indeed the need to, as well, to do this yourself so you'd pick out x, if y is present.

I'm into this, but it seems like the usage of the feature itself, even though how good it is, is undermined when it comes to the actual use case.

From userland, its features should be good for all scales, but this

seems like an single issue and is more about how to retrieve data in a special use case rather than at regular basis.

From SO, there's indeed various of issues in different use cases, but they

seem a lot like architectural issues in react rather than the purest form of ES6. There's different issues, but the same answer.

Taking a stand from the negativity ( finally :) ), it's a good feature, but it seems like we can do this on the plain level without involving new operators.

It's a good feature request in its nature, but the shape of it seems a off, when there's other ways.

Contact me on twitter or mail if you want more input, @ev1stensberg. Again, this is my perspective, if someone have arguments that enforces or makes mine less relevant, please do elaborate!

ES

# Jason Orendorff (8 years ago)

On Tue, Sep 20, 2016 at 2:38 PM, Bob Myers <rtm at gol.com> wrote:

People in the real world continue to wonder why they can't pick/destructure from objects into objects, instead of just variables.

stackoverflow.com/questions/39602360/es6- destructuring-reassignment-of-object?noredirect=1#39602360

This is actually the second such question on SO in the last week.

Well, this not as weighty a consideration as you might think.

Some people on a web site are curious to know if they can reduce 2 lines of JS code to 1 line. Their shared curiosity may be a coincidence; or it may be the language's fault. Presume the latter. It doesn't follow that the answer should be yes. Since all new syntax imposes some mental load on all language users, the answer should be no unless the benefit is really dramatic, which I don't think it is here.

# Matthew Robb (8 years ago)

On Wed, Sep 21, 2016 at 10:40 AM, Jason Orendorff <jason.orendorff at gmail.com

wrote:

Since all new syntax imposes some mental load on all language users, the answer should be no unless the benefit is really dramatic, which I don't think it is here.

For the most part I can completely agree with the sentiment that all new syntax imposes some mental load on all language users but my question is how this applies when syntax RESTRICTIONS that seem counter-intuitive impose their OWN mental load.

I think the inspiration of this thread and the few before it comes from the fact that destructuring syntax and object literal shorthand syntax seem to share in a root syntax yet the two features are incompatible which makes the mental load of the restrictions more obvious.

  • Matthew Robb
# Bob Myers (8 years ago)

Some people on a web site are curious to know if they can reduce 2 lines of JS code to 1 line

Right, some people wanted to reduce 2 lines of JS code

const a = o.a;
const b = o.b;

to 1 line

const {a, b} = o;

and that's how we ended up with destructuring assignment, which managed to make its way into the spec. Actually it's not just one less line; it's more semantic and readable and less bug-prone.

I'm not giving excessive weight to the questions of a few folks on StackOverflow, but it is curious how frequent and obvious the questions of these real people writing real code are: "If I can deconstruct into variables, why can't I deconstruct into objects?"

It's worth pointing out that on a scale of 0 to 100 of syntactical "newness", this proposal ranks quite low. We're not introducing new operators, for example. We're adding the option to specify existing deconstructing syntax after the existing dot syntax to obtain an object with those deconstructed properties.

Another answer to this thread suggested that picking properties into a new object could be handled by userland code. Sure it could, and already is, in the form of Underscore's _.pick etc. So yes, this is syntactic sugar. But so is deconstructing assignment.

But it's a tiny bit more than mere syntactic sugar. Writing obj.{p1, p2} is just sugar for writing {p1: obj.p1, p2: obj.p2}. But I can also write obj.{p1: q1, p2 = 100} to take advantage of the familiar ability of deconstructing syntax to rename and specify defaults, not to mention picking deeply, or using computed property names. That is something that would be very hard to implement in a userland function--ok, maybe not difficult, but cumbersome--do you really want to specify deep picks as "a.b.c"?. And it would be a real challenge to write a TypeScript signature for any version of _.pick, much less one that handled renaming, defaults, and deep picking.

I totally agree with Matthew Robb's comment in a separate response that this proposal is not imposing a new cognitive burden; it's REMOVING the cognitive burden of wondering why our handy deconstructing syntax cannot be used to pick properties into objects.

-- Bob

# Olivier Lalonde (8 years ago)

How about obj{p1, p2} (without the dot)? obj{p1}.p1 === obj.p1 for example seems more elegant to me than obj.{p1}.p1 === obj.p1. I'm not sure if that syntax would be ambiguous though.

# Bob Myers (8 years ago)

Interesting idea, but I'm not too thrilled at the idea of imputing semantics of any kind to the mere juxtaposition of two constructs. And the dot already has the clear meaning of "taking" something (currently, a single property as a scalar) from an object, so it would seem to make sense to extend that existing meaning.

# Jason Orendorff (8 years ago)

On Wed, Sep 21, 2016 at 12:33 PM, Matthew Robb <matthewwrobb at gmail.com>

wrote:

On Wed, Sep 21, 2016 at 10:40 AM, Jason Orendorff < jason.orendorff at gmail.com> wrote:

Since all new syntax imposes some mental load on all language users, the answer should be no unless the benefit is really dramatic, which I don't think it is here.

For the most part I can completely agree with the sentiment that all new syntax imposes some mental load on all language users but my question is how this applies when syntax RESTRICTIONS that seem counter-intuitive impose their OWN mental load.

Neither proposal can reasonably be seen as merely lifting unnecessary restrictions. Emphasizing some words doesn't make this seem like less of a stretch, to me.

I think the inspiration of this thread and the few before it comes from the

fact that destructuring syntax and object literal shorthand syntax seem to share in a root syntax yet the two features are incompatible which makes the mental load of the restrictions more obvious.

If it were a matter of lifting restrictions, this would be a very different conversation. I get that to you, that's all it is. But I have trouble seeing how that is true in any objective sense. In the spec, this will look like a bunch of new text and syntactic productions. And nobody is going to look at {{x, y} = obj} and recover the meaning from previously learned principles of the language. It's an extra thing to learn.

And of course it comes with its own "restrictions", as you put it; i.e. it cannot do everything either. Now people will wonder if they can put some of an object's properties in one new object and dump all the rest in another. Or if there's a shorthand for

let obj = complicated_expression();
let result = {start: {{x, y}=obj}, size: {{w, h}=obj}};

that eliminates the temporary obj. And on and on forever.

# Jonathan Bond-Caron (8 years ago)

On Tue Sep 20 03:38 PM, Bob Myers wrote:

People in the real world continue to wonder why they can't pick/destructure from objects into objects, instead of just variables.

stackoverflow.com/questions/39602360/es6-destructuring-reassign ment-of-object?noredirect=1#39602360

Seems like allowing to "dot" into another identifier could work: tc39.github.io/ecma262/#prod-CoverInitializedName

CoverInitializedName[Yield]: IdentifierReference[?Yield] Initializer[+In, ?Yield] IdentifierReference[?Yield] . IdentifierName

const IDENTIFIER = 1; const sandwichesIWantToEat = { SANDWICHES.CHEESE_STEAK, SANDWICHES.SLOPPY_JOE, IDENTIFIER };

Use the RHS identifier as the member/property name and resolve the "dot" expression to get the value.

const sandwichesIWantToEatResult = { CHEESE_STEAK: SANDWICHES.CHEESE_STEAK, SLOPPY_JOE: SANDWICHES.SLOPPY_JOE, IDENTIFIER: IDENTIFIER };

# Bergi (8 years ago)

Jonathan Bond-Caron wrote:

On Tue Sep 20 03:38 PM, Bob Myers wrote:

People in the real world continue to wonder why they can't pick/destructure from objects into objects, instead of just variables.

Yeah, StackOverflow is hit pretty often with people asking how to do that.

Seems like allowing to "dot" into another identifier could work: tc39.github.io/ecma262/#prod-CoverInitializedName

CoverInitializedName[Yield]: IdentifierReference[?Yield] Initializer[+In, ?Yield] IdentifierReference[?Yield] . IdentifierName

I don't think that's the right grammar rule, but yes, I'd love to see this:

const IDENTIFIER = 1; const sandwichesIWantToEat = { SANDWICHES.CHEESE_STEAK, SANDWICHES.SLOPPY_JOE, IDENTIFIER };

Use the RHS identifier as the member/property name and resolve the "dot" expression to get the value.

const sandwichesIWantToEatResult = { CHEESE_STEAK: SANDWICHES.CHEESE_STEAK, SLOPPY_JOE: SANDWICHES.SLOPPY_JOE, IDENTIFIER: IDENTIFIER };

This simplification in object destructuring and shorthand property intialisers should be easy to add to the language and rather simple to understand. There are no grammar ambiguities, and no completely new productions, but I believe it would help a great deal. Yes, one would still have to repeat the name of the object to/from which the properties to assign/take, but that's usually rather short so it's not a large burden.

I could create a proposal repo, who would like to help?

kind , Bergi

# Jason Orendorff (8 years ago)

On Wed, Sep 21, 2016 at 3:39 PM, Bob Myers <rtm at gol.com> wrote:

Some people on a web site are curious to know if they can reduce 2 lines of JS code to 1 line

Right, some people wanted to reduce 2 lines of JS code [...]

That is not historically how JS got destructuring.

But it's a tiny bit more than mere syntactic sugar. Writing obj.{p1, p2} is just sugar for writing {p1: obj.p1, p2: obj.p2}. But I can also write obj.{p1: q1, p2 = 100} [...]

In destructuring, {p1: q1} = obj means q1 = obj.p1. So, do you mean for this to say {q1: obj.p1}? Or perhaps {p1: obj.q1}; but then why is it inconsistent with destructuring?

You mention deep picking, but I haven't seen what that would actually look like. (Destructuring is not great for deep picking, so I'm not sure how the new syntax requires negative effort to learn because it's all existing parts, and yet it will also solve this problem.)

You mention computed property names. How does that work? In obj.{[p1]}, does p1 mean the variable p1 or the property obj.p1? Assuming the former, is there anyplace else in the language where adding [] affects what an identifier refers to (i.e. scoping)?

How is all of this not a ton of new things for JS developers to learn?

I totally agree with Matthew Robb's comment in a separate response that

this proposal is not imposing a new cognitive burden; it's REMOVING the cognitive burden of wondering why our handy deconstructing syntax cannot be used to pick properties into objects.

This argument is hopeless, and I find it a little weird because a much stronger argument is available to you. You can just say, yes, there is some minor complexity cost here, but it's definitely worth it because of very common use cases X, Y, and Z.

The argument you've chosen instead won't go. None of these proposals could actually be standardized in a way that reduces the total size of the spec. They won't make "JavaScript: The Definitive Guide" a shorter book, either. They all require extra words to explain, whether to implementors or to JS devs. New syntax does not make things simpler, in the same way that up is not down and black is not white.

Cognitive burden is a real phenomenon, where people have to spend time puzzling out unfamiliar ideas and it's pure overhead for them vs. their actual goal. You can't just turn the meaning of the term upside down and be done. It actually means something.

(Also, both you and Matthew seem to think an unlikely point can be carried with the help of allcaps. Admittedly they are very judiciously applied allcaps, relative to the internet at large, but I still don't think this ever works!)

# Bob Myers (8 years ago)

This is a creative idea. I'd rather see it in the language than not. But still half a loaf.

Minor nit: I don't see how this is a "simplification in object destructuring". It has nothing to do with destructuring, right?

I assume that this would work as expected?

{SANDWICHES[mySandwichName]}

to produce

{[mySandwichName]: SANDWICHES[mySandwichName]}

It's a bit sad that I don't see how to get to renaming and defaults with this syntax.

Bob

# Bob Myers (8 years ago)

With regard to deep picking, with standard destructuring I write

const {foo: {bar: { qux }}} = obj;

which means

const qux = obj.foo.bar.qux;

In the proposed "pick/destructure into an object" syntax, I would use identical destructuring syntax

obj.{foo: {bar: {qux}}}

which would turn into {qux: obj.foo.bar.qux}.

We are simply using identical destructuring syntax to destructure into an object instead of a variable.

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? 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.

None of the ES6 enhancements made "JavaScript: The Definitive Guide" a shorter book. Are you saying that means they should not have been included in the spec?

Bob

# Bergi (8 years ago)

Bob Myers wrote:

This is a creative idea. I'd rather see it in the language than not. But still half a loaf.

Yeah, it's not the whole thing, but I believe it's something the TC could agree on before moving further.

Minor nit: I don't see how this is a "simplification in object destructuring". It has nothing to do with destructuring, right?

For consistency and ease of understanding I would apply the same pattern for destructuring cases, i.e.

({o1.x, o1[y]} = o2);

desugars to

({x: o1.x, [y]: o1[y]} = o2); // but `y` being evaluated only once

This would help a great deal where the object on which the properties should be assigned already exists, e.g. in a constructor:

constructor(options) {
     ({this.propA, this.propB, this.optOne} = options);
}

to provide some kind of "selective Object.assign".

It's a bit sad that I don't see how to get to renaming and defaults with this syntax.

Renaming has always been part of the language:

o2 = {y: o1.x} // o2.y = o1.x
({y: o2.z} = o1); // o2.z = o1.y

and with destructuring we'd also get defaults.

# Matthew Robb (8 years ago)

​First I'd like to state that I don't mean to suggest that this is "simple", in practical terms, as either a spec change or in application of the feature in day-to-day code.​

On Thu, Sep 22, 2016 at 11:24 AM, Jason Orendorff <jason.orendorff at gmail.com

wrote:

I get that to you, that's all it is. But I have trouble seeing how that is true in any objective sense. In the spec

​​The point I was trying to make is that "to me" is exactly the perspective I was hoping to share as people writing JavaScript don't have an intimate understanding of the spec more often than not. In teaching people who spend most of their time writing JavaScript where each line starts with $(...) I get more questions about things that look like they should work some way in one place because it actually works that way in another than anything else.

I don't seek to trivialize either side of the argument but with syntax in particular I would hope that complexity additions be back-loaded into the spec rather than front-loaded onto users.

I apologize FOR MY USE OF CAPS TO EXPRESS emphasis. It's a bad habit :P

  • Matthew Robb
# Jason Orendorff (8 years ago)

On Thu, Sep 22, 2016 at 1:14 PM, Bob Myers <rtm at gol.com> wrote:

In the proposed "pick/destructure into an object" syntax, I would use identical destructuring syntax

obj.{foo: {bar: {qux}}}

which would turn into {qux: obj.foo.bar.qux}.

We are simply using identical destructuring syntax to destructure into an object instead of a variable.

I'm sorry, Bob, the link in your original post in this thread has a link to the proposal; it's perfectly clear on this, and I should have gone back to it.

OK, I think I understand the proposal now. A few comments:

  1. The gist says:

    The syntax of the Assignment Pattern is identical.

    I don't think this is right; I think you want all the right-hand sides of all the properties in the tree to match PropertyName. In AssignmentPattern, those can be any LeftHandSideExpression:

    {text: f().q[13].name} = obj;
    

    But that wouldn't make sense in pick notation:

    obj.{text: f().q[13].name}  // ???
    
  2. I mentioned scoping earlier. What I mean is, in expressions like

    obj.{[p]: p}
    

    the two identifiers p mean different things (the first p is a variable name, the second p a property name), which is new and subtle. The same thing happens in obj.{p = p}, except the other way round. Destructuring does just a touch of this in the shorthand case, but I never found that confusing. This is.

  3. I wonder if this can be made more symmetrical -- as it stands, it allows you to flatten a complex object:

    // from a tree of 3 objects to one
    let rect = corners.{
        p0: {x: left, y: top},
        p1: {x: right, y: bottom}
    };
    

    The reverse would be to add structure to a flat object. There's no way to do that, right?

    // turn the 1 object back into a tree of 3?
    let corners = rect.{ ??? };
    

    Likewise, it seems you can turn an array into a plain object, but not the other way around?

    let point = coords.[x, y];   // {x: coords[0], y: coords[1]}
    let coords = point.{???};   // [point.x, point.y]
    

    Honestly even the examples that do work seem ugly to me. I think pretty much all the value in the proposal is in the simplest case, obj.{identifier, ...}.

# Jason Orendorff (8 years ago)

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 .{.

# Bob Myers (8 years ago)

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

# Isiah Meadows (8 years ago)

I'll note that the type checking part will eventually get fixed in TypeScript, at least.

Microsoft/TypeScript#1295

Oh, and I've already suggested another, more specific, pick syntax (that operation is only a shorthand for x => x.property) ,but it ultimately got

rejected since no one could really agree to anything different other than the current state was workable, but not optimal. You may appreciate reading the entire conversation, as it's pretty informative and is related to this.

Here's the specific comment proposing that idea: tc39/proposal-bind-operator#24

Here's the issue where most the related discussion took place: tc39/proposal-bind-operator#26

# Isiah Meadows (8 years ago)

On Wed, Sep 7, 2016, 04:10 Kris Siegel <krissiegel at gmail.com> wrote:

I'm just saying those references still exist, and ignoring them will lead you into more problems than this would solve.

Not suggesting anything of the sort only that your workaround adds additional variables with possible object references and a more native implementation could work around that. Nothing more.

And this isn't C: unless you're using those references, JS engines will clean them up automatically, anyways, because of their garbage collection. My workaround produces 0 more required object references than what the engine needs.

Hmm this is interesting. It's been a while since I last memory profiled a web application but the last time if I kept any references to, say, DOM elements in any variables even if I was no longer using them they would still be eating up the memory causing memory leaks. Are you saying this is no longer the case? You don't need to necessarily get rid of all references to, say, a DOM element before the garbage collector will sweep it up?

If the DOM element is not live and either is not referenced, or referenced only by things that are marked for collection, the GC will mark it (assuming they are only referenced by it). Then after a while, it's swept away with the rest of the garbage.

(Technically, this is an oversimplification, but it's a complex topic. Also, this doesn't apply to really old IE, which uses reference counting instead.)

I'm more curious than anything else; it appears my understanding here might be wrong so I want to dig into it more.

You do, but extra syntax for pick makes 0 difference in this area. My point is that the new references in between are created and retained regardless of how you create them.

Oh, and I've already suggested another, more specific, pick syntax

Good read. Interesting. Honestly I'm more of a fan of introducing functions if possible to handle much of this instead of new syntax though I don't know if that is necessarily supported by others and that proposal goes a bit before what I was originally thinking.

I agree, too. And for individual properties, it is helpful, but this language isn't quite as functional as people want to think. Also, it's not like the alternatives are much larger: x => x.property (and for the

original proposal's shorthand: (...xs) => console.log(...xs)).

# Isiah Meadows (8 years ago)

I'm just saying those references still exist, and ignoring them will lead you into more problems than this would solve. First, my above workaround would likely be the desugaring in V8. You could optimize it by creating the actual object and assigning the properties as they come from the object, but that's also a valid optimization for engines to make today with my workaround. Also, most other optimizations are equally permissible with both syntax and my equivalent workaround.

And this isn't C: unless you're using those references, JS engines will clean them up automatically, anyways, because of their garbage collection. My workaround produces 0 more required object references than what the engine needs.

As you can see, I'm generally against this.

# Isiah Meadows (8 years ago)

Isiah's workaround works but has the unfortunate side affect of copying

values / increasing reference counts to objects. I'd love to see a built in solution.

You'd have to do that anyways, even if it's entirely internal. How do you think you would do it outside of copying the values?

# Kris Siegel (8 years ago)

I'm just saying those references still exist, and ignoring them will lead

you into more problems than this would solve.

Not suggesting anything of the sort only that your workaround adds additional variables with possible object references and a more native implementation could work around that. Nothing more.

And this isn't C: unless you're using those references, JS engines will

clean them up automatically, anyways, because of their garbage collection. My workaround produces 0 more required object references than what the engine needs.

Hmm this is interesting. It's been a while since I last memory profiled a web application but the last time if I kept any references to, say, DOM elements in any variables even if I was no longer using them they would still be eating up the memory causing memory leaks. Are you saying this is no longer the case? You don't need to necessarily get rid of all references to, say, a DOM element before the garbage collector will sweep it up?

I'm more curious than anything else; it appears my understanding here might be wrong so I want to dig into it more.

Oh, and I've already suggested another, more specific, pick syntax

Good read. Interesting. Honestly I'm more of a fan of introducing functions if possible to handle much of this instead of new syntax though I don't know if that is necessarily supported by others and that proposal goes a bit before what I was originally thinking.

# Bob Myers (8 years ago)

I just find the syntax confusing

It combines two syntaxes we know and love: (1) dot notation o.b and (2) deconstructing notation {a} = o, into o.{a}.

Maybe some have problems with the dot in a.{o} being too inconspicuous. At the cost of using up a precious special character, we could go with o # {a} instead. Actually I think o pick {a} is syntactically unambiguous.

I do like the idea of building a way of taking a portion of an object out

of one object and into another

Good, many people seem to agree.

Why couldn't this be an Object.pick()

Personally, passing around properties as strings grates on me. Then there's the issue of typability, although Isiah has pointed out a useful TypeScript proposal that does deal with that. Within the _.pick paradigm, it's hard to envision a concise syntax for defaults and renaming, which the deconstructing-like syntax gives us for free.

It could even be expanded to handle deep nesting

Just curious, what sort of interface to your _.pick-like thing do you have in mind>? Will it support defaults and renaming?

By the way, I was looking at Elm and found it supports one advanced feature of extended dot notation, which is the unary dot as function, as in arr.map(.p).

Bob

# Bob Myers (8 years ago)

I just find the syntax confusing

It combines two syntaxes we know and love: (1) dot notation o.b and (2) deconstructing notation {a} = o, into o.{a}.

Maybe some have problems with the dot in a.{o} being too inconspicuous. At the cost of using up a precious special character, we could go with o # {a} instead. Actually I think o pick {a} is syntactically unambiguous.

I do like the idea of building a way of taking a portion of an object out

of one object and into another

Good, many people seem to agree.

Why couldn't this be an Object.pick()

Personally, passing around properties as strings grates on me. Then there's the issue of typability, although Isiah has pointed out a useful TypeScript proposal that does deal with that. Within the _.pick paradigm, it's hard to envision a concise syntax for defaults and renaming, which the deconstructing-like syntax gives us for free.

It could even be expanded to handle deep nesting

Just curious, what sort of interface to your _.pick-like thing do you have in mind>? Will it support defaults and renaming?

By the way, I was looking at Elm and found it supports one advanced feature of extended dot notation, which is the unary dot as function, as in arr.map(.p).

Bob

# Bob Myers (8 years ago)

Since some people seem to find the proposed pick notation o.{p1, p2}, (to create an object composed of the p1 and p2 properties from o) confusing, I am making an alternative proposal which hews more closely to existing deconstructing assignment.

The definition of the new syntax is:

If a deconstructing assignment is wrapped in curly brackets {}, the

result is a new object where the deconstructed names and values become property names and values.

The above case would be written as:

{ {p1, p2} = o }

and would result in

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

AFAICT the above syntax should be uniquely parseable. The advantage of this syntax is the minimal cognitive footprint: if you know how to deconstruct an object into variables using deconstructing assignment, then you can deconstruct an object into properties on a new object with nothing more than an extra set of surrounding curlies.

I will be adding this alternative syntax to the repo here rtm/js-pick-notation and here rtm/js-pick-notation/blob/master/minimal/spec.md in

the near future.

Bob

# Новиков Денис (7 years ago)

An HTML attachment was scrubbed... URL: esdiscuss/attachments/20171010/d1804e32/attachment