Suggestion: Destructuring object initializer.

# Yeong-u Kim (6 years ago)

Suggestion: Destructuring object initializer.


Destructuring assignment: it extracts values by destructuring an object, and assign them to variables. I suggest Destructuring object initialization syntax: it is similar to Destructuring assignment, except that it initializes an object with the extracted values.

const name_info = {"first name": "Yeong-u", "last name": "Kim", nickname: "K."};
const propname = "computed property name";

const object = {
	name: {
		*{"first name": forename, "last name": surname}: name_info
	},
	*[a, b, c]: [1, 2, 3],
	*[d]: [4],
	[propname]: "This is not part of the syntax",
	*[{"some property name": propname}]: [{"some property name": 5}],
	*{gettable: "something gotten"}: {get gettable() {return Symbol("Using [[Get]]");}}
};
/*
	{
		name: {
			forename: "Yeong-u",
			surname: "Kim"
		},
		a: 1,
		b: 2,
		c: 3,
		d: 4,
		"computed property name": "This is not part of the syntax",
		propname: 5,
		"something gotten": Symbol(Using [[Get]])
	}
*/

In this way, we may not give them a computed property name as with the current Destructuring assignment syntax, though.


I would appreciate hearing your opinion on this.

# Bob Myers (6 years ago)

This extremely useful feature, which is sometimes called "picking", has been discussed extensively on the group, but the "thought leaders" (?) who apparently have the ability to kill a feature by saying "I don't really think it's that important" have failed to get excited about it, although it seems to me to be at least as "interesting" (in terms of the unwritten criteria apparently applied to determine "interesting") as many other features which are progressing through the ES39 life-cycle, and the nature of the TC39 governance process, which gives entirely new meaning to the notion of "design by committee", makes it impossible to find the champion which is the gating factor for the entire process.

Bob

# Bob Myers (6 years ago)

It does make one stop and wonder why the group will endlessly entertain trolls debating whether or not ES6 (or ES5) portends the end of civilization as we know it, while relentlessly ignoring literally dozens of similar/identical proposals for property picking, a feature which easily contributes as much to the language at as little cost as many other features such as spread properties.

Bob

# Andy Earnshaw (6 years ago)

Bob, I think it's an interesting idea too, but you can't strong-arm people into getting excited about what you're asking for. If it really is that important to you then put together a solid proposal, write a Babel plugin and then try to find a champion for it.

# Bob Myers (6 years ago)

Thanks for your input.

Actually, I was not trying to beat the dead horse of my property picking proposal, but rather give advice to another would-be spec proposer based on my experience.

But since you brought it up, the spec for property picking can be found at rtm/js-pick-notation. As with any spec, one could argue that it's too brief, or too verbose, or missing this or that, but actually this is a very simple feature. There is a fair amount of discussion on this list about this proposal, in various iterations, over the last few years.

As for an implementation, the TC39 process documents clearly state that the implementation types expected for Stage 0 (strawman) is "N/A". I'd be glad to write a Babel or sweet.js implementation, but I'm not quite sure what it would prove.

Although the TC39 documents are murky on this point, and some of them appear to state that a proposal can gain Stage 0 status without a champion, other information seems to say that getting a proposal to Stage 0 DOES require the involvement of a TC39 member, even if they are not technically considered a "champion" at that point...As for "trying to find" such a champion, I thought posting to this group constituted such an effort, and in addition I have reached out to a couple of members with no response.

Here's a real quick intro to the proposal:

const {p1, p2} = p;
const [q1, q2} = q;
return {p1, p2, q2, q2};

==or==

return {p1: p.p1, p2: p.p2, q1:q.q1, p2: q.q2};

becomes

return { {p1, p2} = p, {q1, q2} = q };

Yes, it's pretty much sugar--no brand new functionality here. It's about brevity and expressiveness, which seems to have been a low enough bar for several other features to pass. It steals no new symbols. It clearly leverages existing destructuring assignment syntactical infrastructure.

Bob

# Naveen Chawla (6 years ago)

I'm not a TC39 member, but I have a little readability issue with destructuring inside an object:

{ {p1, p2} = p, {q1, q2} = q }

has a very different meaning than

{ p: {p1, p2}, {q1, q2} = q }
# Naveen Chawla (6 years ago)

Sorry sent by accident before my message was edited properly. My basic point was that since curly braces are used for both destructuring and object literals, there's an issue with being able to glance at the code and quickly discern what's happening if they are mixed together in the same piece of syntax. Not impossible, just a potential source of bugs and/or delay in understanding the data structure being declared.

# Bob Myers (6 years ago)

Thank you for your comments.

The proposed picking syntax has gone through several iterations. Way back when, we had

o # {a, b}

for picking properties a and b from o, resulting in {a: o.a, b: o.b}. That, however, would of course eat a precious symbol. So a later version replaced the # with the dot ., which makes some semantic sense, since the dot has been used for two decades to "pick" a property into a value; that is the reason that in some versions this proposal was called "extended dot notation". This looked like:

o.{a, b}

This proposal had the benefit of a narrow syntactic footprint, since the grammar has never allowed anything other than an identifier to follow the dot. On the other hand, it had the downside that the dot was not very visible.

(I unfortunately then muddied the waters by trying to extend the dot in additional ways, such as bracket-less array indices, finally solving the array#first and array#last problems by allowing array.0 and array.-1. But I digress.)

But then I considered that we might want to pick from two or more objects into a single new object, and I didn't see how to do that easily with this syntax. But now we have spread properties, so we could write

{...p.{p1, p2}, ...q.{q1, q2}}

But there was another consideration, which was consistency of syntax between picking (destructuring) into variables, versus picking into objects. Since ES6 we have had the {p2, p2} = p syntax for picking into variables. So the notion was to use precisely this destructuring assignment syntax, complete with its features for defaults, renaming, and nesting, for picking into objects, simply by placing it inside an object literal. That's how I ended up with the current proposal, which is

{ {p1, p2} = p, {q1, q2} = q }

But I still think the so-called "extended dot notation" version is entirely viable.

Looking back over some old threads, I see comments like:

The first issue (in chronological order) with that proposal is the lack of motivation. What is the problem that the proposal is trying to solve?

Whatever one can say about this problem or proposed solution does not include it not being well-defined. The problem statement is that we want to pick properties p1, p2, and p3 from one object p into another object, in a more concise, brief, less repetitive, less error-prone way than saying {p1: p.p1, p2: p.p2, p3: p.p3}. I wonder if that is really that hard to understand. Whether or not that's important is a different question, of course.

The reason I started thinking about this issue, eons ago, is that I ran into it, regularly, in my own programming work, since I'm an active programmer who designs and codes much of the day. This question of how to "pick" from objects into objects also comes up over and over again on Stack Overflow. The current "best" "one-line" solution, at least by number of upvotes (provided by me, before I went cold turkey) was

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

This requirement/need/desire has also been raised on this forum half a dozen times by different folks over the years.

Of all the reactions to this proposal, other than "who cares", one of the most common is that we already have _.pick in popular libraries. Well, something being in a library can just as easily be interpreted as meaning it's a good candidate for being added to the language, as meaning that it doesn't need to be. Also, implementing this in user-land also requires quoting the property names, as in _.pick(p, ['p1', 'p2']), which is awkward at best. New features in Typescript allow you to write your own pick-type operators in a more type-safe way than was the case previously, but still it requires some gymnastics.

Another common reaction is "big deal, saving a few characters or lines". But more than one recent language feature also falls into this category of mainly or purely sugar and brevity. For instance, property spread syntax is pretty much just sugar for Object.assign, yet everyone seems to think it's the best thing since sliced bread.

Bob

# Bob Myers (6 years ago)

write a Babel plugin

As far as I know, the plugin architecture to Babylon, Babel's parser, is not open, and the parser cannot be extended. What is open to regular people is the ability to write Babel plugins to analyze or transform the AST. The only alternative for new syntax at the moment would be to fork Babylon. Let me know if you know something I don't.

From thejameskyle/babel-handbook/blob/master/translations/en/plugin-handbook.md :

Note that Babylon has not yet opened this API to external plugins, although may do so in the future.

Bob

# Isiah Meadows (6 years ago)

Another common reaction is "big deal, saving a few characters or lines".

But more than one recent language feature also falls into this category of mainly or purely sugar and brevity. For instance, property spread syntax is pretty much just sugar for Object.assign, yet everyone seems to think it's the best thing since sliced bread.

My understanding of this differs:

  • It was to bring feature parity with array spread destructuring.
  • The proposal included both merging and extracting.
  • It actually optimized for an exceptionally common case (React circles benefitted the most, but others did quite a bit, too), immutable update of record-like objects. In my experience, that scenario is more common than array spread (beyond rest parameters), and engines can optimize for objects that are not referenced elsewhere by not actually copying them, something harder to do with Object.assign.*
  • You could implement it via Object.assign, but it chopped the number of characters down to less than half for most cases if you're not just merging. Most of these pick proposals aren't coming close.

* I don't know/believe if they do, but it's pretty easy to implement with type info.

# kai zhu (6 years ago)

inline counter-rant.

On Feb 8, 2018, at 9:04 PM, Bob Myers <rtm at gol.com> wrote:

It does make one stop and wonder why the group will endlessly entertain trolls debating whether or not ES6 (or ES5) portends the end of civilization as we know it, while relentlessly ignoring literally dozens of similar/identical proposals for property picking, a feature which easily contributes as much to the language at as little cost as many other features such as spread properties.

because most language-proposals are not important (or worse distractions) when you look at the really hard engineering-problems javascript was built to solve: UX design and [async] UX workflows (which are outside the comfort-zone of most engineers). people who have zero-empathy for UX are generally not going to become successful javascript-programmers (and their javascript-language opinions weighted accordingly). many just end up creating “correct” but non-user-friendly tools and libraries that no frontend-engineer wants to touch.

companies do not hire laid-off engineers retrained as javascript-programmers so they can continue doing the things that made them redundant, like working on micro-solutions to “hard” cs-problems beaten-to-death by college-professors, that many times end up as re-inventions no better off than sqlite3. they hire javascript-programmers to solve higher-level UX problems, such as writing the necessary messy/ugly integration-code to leverage existing good-enough data-structures like sqlite3 into a UX solution like a persistent amazon shopping-cart, or facebook image-uploader. destructuring doesn’t solve UX problems or add value to employers hiring javascript-programmers. its a negative-productivity language-feature that makes already messy integration-code even harder to read, especially for those trying to debug code not written by themselves.

# Jerry Schulteis (6 years ago)

I had the same concern at first about confusing the destructuring braces with a nested object literal. After looking at it some more it seems like a natural extension of destructuring assignment.

# Naveen Chawla (6 years ago)

A big thing in a programming language for me is "glanceability" (ability to glance at code and discern roughly what's going on).

Allowing destructuring braces to be mixed in object literals may well be a natural extension of destructuring: I'm not denying that.

I just think that since they unfortunately share the same symbols for different-meaning things, I think that a reader of the code may accidentally misunderstand the structure of the object literal and presume to access its properties wrongly, leading to a bug and wasted time. This for me is a counter-rationale for the (or any new) feature in the language.

However, if it was more visually discernable, then it wouldn't have this issue.

# Bob Myers (6 years ago)

Thanks for reading my post down to the part that caught your attention. Concerning this:

engines can optimize for objects that are not referenced elsewhere by not

actually copying them, something harder to do with Object.assign.*

I'm intrigued; please elaborate. I had thought that {...a, ...b} was 100% identical in every way to Object.assign({}, a, b).

* I don't know/believe if they do, but it's pretty easy to implement

with type info.

What kind of type info did you have in mind? Type info based on static analysis currently performed and/or theoretically able to be performed by engines against ECMAScript 20xx programs? In that case, why couldn't such type inferencing be applied to optimizing the Object.assign-based construct? Or do you mean type info from some kind of type annotation system in the future?

Bob

# Alexander Jones (6 years ago)

The difference between {...a, ...b} and Object.assign({}, a, b) is the same as the difference between [a, b] and new Array(a, b). The latter cases (correct me if I'm wrong) rely on dynamic lookup of names and methods, whereas the former cases, as syntactical constructs, have statically defined semantics. Not really sure if modern engines are able to optimize around this yet. I'm sure someone here knows...

# Isiah Meadows (6 years ago)

BTW, regarding engine optimizability of those, I've filed a couple V8 bugs:

  • Object.assign)

There are things engines could do that they aren't currently doing. Part of why I proposed V8 take a look at this is because it has one of the more flexible IC systems out of all the engines (they can lower Array.prototype.map to a simple loop for dense arrays even though a simple delete array[index] within the loop breaks the assumption - this is exceptionally difficult to implement with the ability to deoptimize).

Isiah Meadows me at isiahmeadows.com

Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com

# Bob Myers (6 years ago)

Cool. I don't claim to fully understand this, but as I read your issue, it seems the optimization could/would apply to either spread properties OR Object.assign case. If that's true, then there's nothing specially optimizable about spread properties, in which case that particular point would NOT have been a reason to support its adoption. Or is there some nuance I'm missing?

# Isiah Meadows (6 years ago)

The nuance is that it is far more concise, making it more useful for frequent immutable updates, and it brings consistency with array spread (it was briefly considered to make a symbol for customizing object spread, but it was ultimately rejected for reasons I can't remember). Also, the optimizations would be much less speculative (you could perform them at the baseline level for object spread with some effort, unlike with Object.assign).

# Bob Myers (6 years ago)

Thanks for engaging.

This entire little sub-thread, and my question about what nuance I was missing, was related to one single item on your useful list of reasons why property spread was a good idea, namely your assertion that property spread permits engine optimizations which are different, or better, or easier, than those possible with Object.assign.

I asked about this specifically because I had been under the impression that {...a, ...b} is semantically equivalent in every regard to Object.assign(a, b). Then I was hoping that the issue you posted to the V8 issue tracker would cast light on the difference, but could not see anything there that helped me understand it either. On the contrary, in your post as I read it you seem to be assuming/implying that the two cases are equivalent in terms of opportunities for optimization.

I am perfectly willing to consider reasons for bringing in property spreads such as "parity" or "brevity". It's a separate discussion as to whether or not those other criteria do or do not apply, or to what degree they might apply, to property picking. First, I just want to understand the point about engine optimization, because if that is actually the case it would seem to be a very compelling argument. The simple version of the question is, are there or are there not engine optimizations specifically made possibly by property spread syntax which would not be possible with Object.assign, and if so what are they?

Bob

# Richard Gibson (6 years ago)

I'm unsure if Object.assign(a, b) and {...a, ...b} are equivalent in terms of optimization opportunities, but I am certain that they are not equivalent in every regard because Object.assign invokes setters but object spread does not.

cf. tc39/ecma262#1069 :

// throwslet a = Object.assign({ set x(v) { throw v } }, {x: 0}); // copies propertieslet b = { set x(v) { throw v }, ...{x: 0} };

# Bob Myers (6 years ago)

Thanks for pointing that out, but remember that the Object.assign equivalent of {...a, ...b} is

Object.assign({}, a, b)

In other words, you are always starting off with an empty object, with no setters lying in wait to throw or do anything else. So the question is, are there any differences in terms of engine optimizability between the above and {...a, ...b}?

and Bob

# Raul-Sebastian Mihăilă (6 years ago)

Bob, it's not clear that you understood what Alexander said about the lookup. Object.assign contains 2 names. 'Object' needs to be looked up in all the execution contexts chain, starting with the current execution context, until the global Object function is found. Then the 'assign' property must be found on that object (it might have been deleted since it is configurable). The syntactic form doesn't require these lookups.

Regarding Object.assign({}, a, b), the empty object can inherit a setter which would be called.

# Bob Myers (6 years ago)

Actually, I did understand what he said. I didn't respond because it was obviously wrong.

For example, I did a quick test comparing [1, 2] and new Array(1, 2) and found that performance was identical, in spite of the claimed inefficiency supposedly introduced by the need to look up Array.

I did another test comparing {...a, ...b} and Object.assign({}, a, b) and found that, at least on the platform I was comparing on, the Object.assign alternative was actually a bit faster.

In any case, this optimization (skipping some lookups) is completely separate from the optimization that Isiah was talking about.

With regard to the issue of the empty object inheriting a setter, my we're really clutching at straws here. If you really are worried about someone polluting the Object prototype with a setter, which is something any optimization could account for anyway, then for the equivalent of {...a, ...b} please use Object.assign(Object.create(null), a, b). NOW someone please tell me what opportunities the engine has for optimizing the first but not the second.

Bob

# Raul-Sebastian Mihăilă (6 years ago)

On Wed, Feb 14, 2018 at 8:25 AM, Bob Myers <rtm at gol.com> wrote:

With regard to the issue of the empty object inheriting a setter, my we're really clutching at straws here.

Really? I was simply informing you that Object.assign({}, a, b) is not equivalent to {...a, ...b}.

# Raul-Sebastian Mihăilă (6 years ago)

On Wed, Feb 14, 2018 at 8:25 AM, Bob Myers <rtm at gol.com> wrote:

For example, I did a quick test comparing [1, 2] and new Array(1, 2) and found that performance was identical, in spite of the claimed inefficiency supposedly introduced by the need to look up Array.

I'm curious how you did that testing. Could you post is here?

# T.J. Crowder (6 years ago)

On Wed, Feb 14, 2018 at 6:25 AM, Bob Myers <rtm at gol.com> wrote:

Actually, I did understand what he said. I didn't respond because it was obviously wrong.

It was obviously right. In the message that he replied to you said:

I had thought that {...a, ...b} was 100% identical in every way to Object.assign({}, a, b).

You didn't say "Identical in effect if you disregard potential performance optimizations which the engines I checked don't seem to do based on a quick test I did." You said "100% identical in every way." To then throw "obviously wrong" at the man when he points out how they aren't "100% identical in every way" is -- to put it mildly -- not polite discourse.

There are at least two differences:

  1. As Alexander Jones quite correctly pointed out, Object.assign({}, a, b) requires looking up Object in the scope chain and then requires looking up assign on the object it finds. Engines are required to do that or behave as though they did. They can optimize the heck out of it (as a non-engine-implementer I can easily think of ways to optimize for the overwhelmingly-common case where those are their default values), but that's a difference. If you shadow or reassign Object, or (yikes) modify or delete Object.assign, spread still works; Object.assign doesn't.

  2. Object.assign({}, a, b) necessarily involves a function call; {...a, ...b} doesn't. Again, engines are required to perform a function call, or behave such that they did. And again, in my naïveté I can think of a way to optimize for the overwhelmingly-common-case there, but that's optimization. It's another difference.

Those differences may or may not translate into performance differences once spread support matures (last I checked, a couple of months ago, I was surprised to find the fairly-new spread was markedly slower than Object.assign, but presumably that will change as the new stuff beds in). But they are differences.

So no, Object.assign({}, a, b) and {...a, ...b} are not 100% identical in every way.

-- T.J. Crowder

# T.J. Crowder (6 years ago)

On Wed, Feb 14, 2018 at 7:03 AM, Raul-Sebastian Mihăilă < raul.mihaila at gmail.com> wrote:

On Wed, Feb 14, 2018 at 8:25 AM, Bob Myers <rtm at gol.com> wrote:

For example, I did a quick test comparing [1, 2] and new Array(1, 2) and found that performance was identical, in spite of the claimed inefficiency supposedly introduced by the need to look up Array.

I'm curious how you did that testing. Could you post is here?

FWIW: jsperf.com/literal-vs-new-array-in-nested-context

new Array(x, y) does seem to be nicely optimized. Interestingly, the one-argument version (passing a non-number as the first argument to avoid that weirdness in new Array) is hugely slower on Firefox. But presumably that's because of the branch it has to take to check the type of the parameter (which is something it dosen't share with Object.assign vs spread). In the 2+ argument case, they're about the same, at least in that test.

-- T.J. Crowder

# Bob Myers (6 years ago)

My apologies if I was not clear. What I am asking is just

Please explain the potential for engine optimizations offered by property spread syntax {...a, ...b} which is not available or as easy with the alternative Object.assign({}, a, b).

Kindly note that there is no need for purposes of this thread to comment on:

  1. Any optimizations related to not needing to look up the Object symbol or its assign method, since the engines have apparently already figured out and implemented that optimization, and therefore in and of itself that optimization does not constitute an argument in favor of property spread syntax, which is the context of this discussion.

  2. Any behaviors caused by non-standard characteristics of {} which is used as the first argument to Object.assign, such as setters on Object.prototype, since not only should the engines be able to exclude such cases from its optimizations, but in any case any such issues could be removed from consideration by using as the alternative Object.assign(Object.create(null), a, b), and therefore this point also does not affect the point of the discussion, which is whether or not property spread syntax offers unique opportunities for optimization not heretofore present.

  3. Other aspects of property spread syntax which supported its adoption, as valid or convincing as such arguments might be, since those can and will be discussed separately; this particular discussion is about whether or not, and if so in what matter, property spread syntax offers NEW optimization opportunities to the engines.]]

Bob

# Isiah Meadows (6 years ago)

There currently are not, and I can tell you that the suggested optimization for Object.assign would be substantially more complex. It's possible they may turn down the Object.assign optimization until later, also - they don't perform optimizations based on the way a function is called beyond argument count/type currently, which also prevents them from eliding the return value allocation in the common case of splice-to-remove-from-arrays.


Isiah Meadows me at isiahmeadows.com

Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com

# Isiah Meadows (6 years ago)

That was mentioned in the V8 bug, so I'm aware. (I left that implied, thinking others would catch on.)

Isiah Meadows me at isiahmeadows.com

Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com

# Isiah Meadows (6 years ago)

That's odd/interesting, but for the purposes of my bug, that is covered as part of the "no previous fields have been assigned to the literal", and thus would prevent the optimization statically in either case. However, arrays could afford looser restrictions (since 1. properties aren't preserved, and 2. you can always just bulk-move properties using intrinsics).

Isiah Meadows me at isiahmeadows.com

Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com