Proposal: named and bound deconstructions

# Michael Rosefield (9 months ago)

Deconstructions are great, but have some pain points I'd like to address.

Named deconstructions

When deconstructing elements of an object/array, you lose any reference to the object itself. That reference is useful, not only in the trivial way of being able to simply use it as per normal, but to clarify intent.

const someMiddlewareFn = (original, updated, next) => {
  const [ { name }, { name: newName } ] = [ original, updated ]; // any
useful deconstructions
  if (name !== newName) { // do stuff }
  return next(original, updated);
}

In that example we can't deconstruct original or updated in the parameter definition, because we have to keep references to them to pass to next; we have to do it in the function body, instead.

By keeping the named parameters, though, we glean more information as to the intent of the function: original and updated directly imply that we're comparing state changes.

There might also be occasions when we want to do something like this:

const { bar } = foo,
      { baz, blah } = bar;

Here, we might find it useful to get deconstructed references to both bar and its properties baz and blah, but we have to do it in two steps.

I propose that we use a symbol to signify that we wish to keep a reference, in passing. We should also allow for renaming, which is made slightly awkward because the current renaming syntax prevents further deconstruction.

Suggestion (using !-prefix)

const { !bar: { baz, blah } } = foo, // no renaming
      { bar: !myBar : { baz, blah } } = foo, // renaming

     someMiddlewareFn = (!original: { name }, !updated: { name: newName },
next) => {
        if (name !== newName) { // do stuff }
        return next(original, updated);

I think that this should be clear to interpret for both coders and JS engines.

Bound deconstructions

Some methods rely on their context being bound to their containers, and require extra binding faff if you want to extract them; this is the pain-point area that the binding operator proposal tc39/proposal-bind-operator is intended to address.

My 2nd suggestion is that we co-opt its :: syntax for use in deconstructions:

const { ::foo } = bar;
// equivalent to
// const foo = bar.foo.bind(bar)
# Isiah Meadows (9 months ago)

What can this do that a second variable declaration can't, apart from the auto-binding syntax (which already feels a little more like a tack-on feature rather than something that fits)? And beyond a "that's nice" kind of thing, how would this bring substantial benefit to the programmer?

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

# Michael Rosefield (9 months ago)

Well, deconstruction is already mostly just syntactic sugar to accomplish related things in a single, concise, declarative step; this seems like a natural extension of that philosophy.

Why add extra steps like below when you don't have to? I want JS to be as expressive as possible.

const { foo, bar, baz } = obj;
[ foo, bar, baz ].forEach(fn => fn.bind(obj);
// VS
const { ::foo, ::bar, ::baz } = obj;
# Jerry Schulteis (9 months ago)

I don't think declaring and destructuring formal parameters all at once improves clarity.Once you get beyond a toy example, the parameter list, like a sentence with many phrases, becomes awkward, causing readers difficulty in following the meaning. Technical writing guidelines include the use of concise sentences. I opine that this guideline applies to code as well. Your proposed syntax puts the reference to the whole on the left and the destructuring pattern on the right, the opposite of destructuring assignment. The existing meaning of the exclamation mark is to negate the meaning of what follows.That seems an odd choice to overload with the meaning "keep a reference".C++ uses the ampersand for declaring a reference. Not everyone knows C++, but at least it has some basis in an existing language. If I felt this was needed, my version would look like this:const someMiddlewareFn = ({name} &original, {name: newName} &updated, next) => {

However, I like to think of ECMAScript as somewhere near the happy medium on a programming language continuum that puts the terseness of APL at one extreme and the verbosity of COBOL on the other. I think this is an unnecessary nudge in the terse direction.

# Isiah Meadows (9 months ago)

I'm just not convinced it adds anything substantial. Destructuring made it possible to flatten 3-5 lines to 1, along with the ability to provide defaults. This just:

  1. Lets you give a name to a temporary object, when an extra statement does this for you. (It's rare to want to alias more than one thing.)
  2. Let's you auto-bind a property when extracting it. (This could be suggested in an issue in the bind operator's repo, and is more relevant there.)

Also, with recent talks about making operator-bound callbacks idempotent in the bind operator proposal, that could reduce bound methods to just a simple property-like "access".


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 (9 months ago)

Destructuring made it possible to flatten 3-5 lines to 1, along with the ability to provide defaults.

Yet, as an aside, this "principle" of "flattening" being a sufficient or at least necessary condition for syntax proposals, is itself not applied consistently, as seen in the "rejection" (if lack of positive response on this mailing list can be deemed "rejection") of the proposed property-picking syntax, which allows one to replace three lines

const {a, b} = foo;
const {c, d} = bar;
return {a, b, c, d};

with one:

return { {a, b} = foo, {c, d} = bar };

Actually, by the way, destructuring is of course about more than brevity. It mitigates a common type of typo-related bug:

const misspelledProp = foo.mispeledProp;

Bob

# Darien Valentine (9 months ago)

There might also be occasions when we want to do something like this:

const { bar } = foo,
      { baz, blah } = bar;

Here, we might find it useful to get deconstructed references to both bar and its properties baz and blah, but we have to do it in two steps.

Unless I’m misunderstanding what you mean, doing this in a single pattern is already accounted for:

const { bar, bar: { baz, blah } } = foo;

There’s no restriction on the number of times the key appears.