[EXTERNAL] Re: Destructuring by &reference

Andrea Giammarchi andrea.giammarchi at gmail.com
Thu Mar 4 21:41:09 UTC 2021


in PHP &$ref means by reference ... and in C is about the address ... I
understand your proposal, but I see few gotchas in it:

```js
let { x: ref x } = obj;
```

my proposal is:

```js
let {&ref} = obj;
```
less typing, less eyes crawling, it's about describing the intent to
change, or address, `obj.ref` directly through that `ref`.

one thing I am not seeing anywhere, is my idea of having ref able to
address accessors too (getters and/or setters).

```js
let obj = {
  _: 'value',
  get value() { return this._; },
  set value(str) { this._ = str; }
};

let {&value} = obj;

value; // "value"
value = "other"; // setter triggered
value; // "other"
```

The other thing I don't see in your examples, is implicit method binding:

```js
let counter = {
  _: 0,
  get value() { return this._; },
  increment() {
    this._++;
  }
};

let {&increment, &value} = counter;

// increase the counter by 1 each time
setInterval(increment, 1000);

// at any time
console.log(value); // 0, 1, 2, 3 ...
```

So, despite myself not having strong feelings about using `&` as prefix,
even if it reasons with some other PL somehow, and works well with non
allowed chars as property, variable, name, where `&[Symbol.for('thing')]`
would work just fine, I'd love to have a "pointer" to that thing I want to
deal with, without having *noise* around, the whole object, so that the
amount of scoped references per block/function are reduced, and their
intent more explicit than ever, with less surprises.

I hope this better explains what I am after


On Thu, Mar 4, 2021 at 7:10 PM Ron Buckton <Ron.Buckton at microsoft.com>
wrote:

> This was mentioned up-thread, but I wrote up this proposal several years
> ago (https://github.com/rbuckton/proposal-refs) and am still considering
> bringing it to committee at some point. However, there are a larger set of
> cross-cutting concerns for refs in the context of a proposal like
> https://github.com/rbuckton/proposal-struct (nee. “Typed Objects” or
> “Value Types”), as well as interop with WASM, that also need to be
> considered. In the refs proposal I currently use `ref` rather than `&`,
> since `&` is often ascribed to unmanaged memory addresses in many
> languages, while `ref` (at least in C#) is specifically tied to references
> to memory managed by the GC. That proposal explainer currently includes
> examples for `ref` variables, parameters, expressions, destructuring, and
> support for reified `Reference` objects.
>
>
>
> In userland I currently have https://esfx.js.org/esfx/api/ref.html as a
> `ref` like mechanism:
>
>
>
> ```js
>
> Import { ref } from "@esfx/ref";
>
>
>
> // reference passing
>
>
>
> function f(ref_x1, ref_x2) {
>
> console.log(ref_x2.value); // prints 0
>
> ref_x1.value++;
>
> console.log(ref_x2.value); // prints 1
>
> }
>
>
>
> let x = 0;
>
> const ref_x1 = ref(() => x, _ => x = _);  // mutable ref to a variable
>
> const ref_x2 = ref(() => x); // immutable ref to a variable
>
> f(ref_x1, ref_x2);
>
> console.log(x); // prints 1
>
>
>
> const ar = [0];
>
> const ref_ar0_1 = ref.at(ar, 0); // mutable ref to a property
>
> const ref_ar0_2 = ref.at(ar, 0, /*readonly*/ true); // immutable ref to a
> property
>
> f(ref_ar0_1, ref_ar0_2);
>
> console.log(ar[0]); // prints 1
>
> ```
>
>
>
> Userland destructuring support isn’t feasible, however.
>
>
>
> In my refs proposal, you have several constructs:
>
>
>
>    - `ref` expressions – These take a binding and create a reified
>    `Reference` object from them. Examples:
>       - `let rx = ref x`
>       - `let rfoo = ref obj.foo`
>       - `let rel = ref ar[0]`
>    - `ref` declarations – These take a reified `Reference` object and
>    create a local binding that dereferences them. Examples:
>       - `let ref x2 = rx` – mutable reference
>       - `const ref foo2 = rfoo` – immutable reference
>       - `function f(ref foo) { … }` – mutable reference argument
>       - `function f(const ref foo) { … }` - immutable reference argument
>    - Reified `Reference` objects – These are runtime objects with a
>    `value` property:
>       - If the reference is mutable, `value` has both a getter and a
>       setter.
>       - If the reference is immutable, `value` has only a getter.
>
>
>
> If the `ref` syntax in my proposal were to be adopted, the above example
> would instead read:
>
>
>
> ```js
>
> function f(ref x1, ref x2) {
>
> console.log(x2); // prints 0
>
> x1++;
>
> console.log(x2); // prints 1
>
> }
>
>
>
> let x = 0;
>
> f(ref x, ref x);
>
> console.log(x); // prints 1
>
> ```
>
>
>
> In Augusto’s example, you would have your choice of object passing or
> variable passing:
>
>
>
> ```js
>
> function foo(ref value) {
>   value = 'foo';
> }
>
> function second(source) {
>
>   {
>
>   let { ref value } = source; // NOTE `value` is a `Reference` object here
>     console.log(typeof value); // object
>     foo(value);
>
> }
>
>
>
> // The above would be the same as this
>
> {
>     let value = ref source.value; // `value` is a `Reference` object here
>
>   console.log(typeof value); // object
>     foo(value);
>   }
>
> }
>
>
>
> second({ value: "bar" });
>
> ```
>
>
>
> I’m still considering the destructuring side of things. Whether you are
> creating a `Reference` or dereferencing it is clear for some patterns:
>
>
>
> ```js
> // (a) `ref` Destructuring Targets
>
> // dereferences `obj.x` if `obj.x` is a `Reference`
>
> let { x: ref x } = obj;
>
> // This is equivalent to the following:
>
> let ref x = obj.x; // Probably not what you want…
>
>
>
>
>
> // (b) `ref` Destructuring Bindings
>
> // creates a `Reference` for `obj.x` and stores it in `x`, so `x` is a
> reified `Reference`.
>
> let { ref x: x } = obj;
>
>
>
> // This is equivalent to the following:
>
> let x = ref obj.x; // Probably not what you want either…
>
>
>
>
>
> // (c) `ref` Destructuring Targets **and** Bindings
>
> // Create a `Reference` for `obj.x` and dereference it in `x`:
>
> let { ref x: ref x } = obj;
>
> // This is equivalent to the following:
> let ref x = ref obj.x; // Probably what you wanted
> ```
>
> However, this is less clear for shorthand destructuring assignments or
> array destructuring:
>
>
>
> ```js
>
> let { ref x } = obj; // did you mean (a), (b), or (c) above?
>
> let [ref x] = ar; // did you mean (a), (b), or (c) above?
>
> ```
>
>
>
> In these two examples, you **probably** want (c), but there are valid
> reasons for wanting (a) or (b) as well. The explainer for the proposal
> currently chooses (a), but I’ve been reconsidering. None of this is set in
> stone (since this proposal isn’t even at Stage 1 yet), and I’m open to
> suggestions and discussion on the issue tracker.
>
>
>
> Ron
>
>
>
> *From:* es-discuss <es-discuss-bounces at mozilla.org> * On Behalf Of *Andrea
> Giammarchi
> *Sent:* Thursday, March 4, 2021 12:43 AM
> *To:* Augusto Moura <augusto.borgesm at gmail.com>
> *Cc:* es-discuss <es-discuss at mozilla.org>
> *Subject:* [EXTERNAL] Re: Destructuring by &reference
>
>
>
> > How will you prevent the passing of the object down the pipe?
>
>
>
> ```js
>
> const downThePipe = ({&source}) => {
>
>   // you can read source
>
>   source;
>
>   // you can set source
>
>   source = 'blah';
>
>   // you can't know where source comes from
>
>   // but you could propagate that reference further
>
>   evenFurtherDown({&source, any: 'value'}, Math.random());
>
> };
>
>
>
> downThePipe({
>
>   secret: 'nothing out there can reach me',
>
>   get source() { 'this object'; },
>
>   set source(value) {
>
>     console.log('hello', value, this.secret);
>
>   }
>
> });
>
> ```
>
>
>
> You can pass objects already in JS so this changes nothing in terms of
> logic, except the callback has a way to signal reactive properties or
> retrieved methods.
>
>
>
> Any boilerplate with Proxies would be slower and more convoluted, so this
> syntax simplieis all the code you wrote via an explicit intent: the
> callback would like to invoke, or update an accessor, of the given object,
> without holding, or having, the whole object in its scope.
>
>
>
> I hope this explains a bit better why I think this feature would be
> extremely cool. Polyfills won't need to do much, code remains short and
> clean, accessors/reactive properties becomes instantly clear (they say
> accessors are a footgun, here we're flagging these for better awareness)
> and methods can be invoked with the right context too, without needing
> whole objects references around.
>
>
>
>
>
> On Wed, Mar 3, 2021 at 9:03 PM Augusto Moura <augusto.borgesm at gmail.com>
> wrote:
>
> > that's basically the entirety of the syntax sugar proposals since
> ES2015, right?
>
> Definitely no, but talking about the syntax additions since ES2015, they
> are in one or more of the categories below:
> - avoid known footguns in the language (arrow functions and lexical this,
> classes and prototype, let/const and block scoping, nullish coalescing
> operator, etc.)
> - syntax sugars with strong community feedback AND battle proven prior art
> (classes, destructuring, string templates, rest, spread and default
> values, async/await, etc.)
> - introducing or specifying new mechanisms that didn't exist before in
> ecma (modules, classes, varargs, etc.)
>
> > also proxy and globalThis are *really* unrelated to this
>
> Proxy and globalThis (and the `with` statement for that matter), are
> mechanisms of value indirection aside from the "classic" instance properties
>
> >  while leaking objects all over down the pipe is my major concern,
> something this proposal avoids, as no code will have a reference to the
> entirety of the source object, they'll deal with a known property name
> passed by reference, incapable of changing anything else in the source
> object ... so it's rather a signal, than a convention.
>
> How will you prevent the passing of the object down the pipe? You mean the
> reference variable being passed to another function and setting the prop
> into the source object?
> ```js
> function foo(source) {
>   let { &value } = source;
>   value = 'foo';
> }
>
> function second(source) {
>   // You still need to pass the object forward right?
>   foo(source)
>
>   // Or the proposal is something like this
>
>   let { &value } = source;
>   foo(value);
>   // and then if foo sets the value argument it should reflect in source
>
> }
> ```
>
> Also the usual way of preventing the "passing the full object down"
> problem is restricting the contract with other functions using a
> wrapper/proxy, a well defined more specific interface or in the readonly
> case just omitting the other properties
>
> ```ts
> // Wrapper way
> class Nameable {
>   constructor(instance) { this.#obj = instance }
>   get name() { return this.#obj.name
> <https://nam06.safelinks.protection.outlook.com/?url=http%3A%2F%2Fobj.name%2F&data=04%7C01%7Cron.buckton%40microsoft.com%7C19322e83dccd4cbec00108d8dee98ba7%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637504442359030680%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&sdata=auhWOITedp4copnL22GZZCvMxwDTOn7%2FC2BIgQZ3HQM%3D&reserved=0>
> }
>   set name(newName) { this.#obj.name
> <https://nam06.safelinks.protection.outlook.com/?url=http%3A%2F%2Fobj.name%2F&data=04%7C01%7Cron.buckton%40microsoft.com%7C19322e83dccd4cbec00108d8dee98ba7%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637504442359040638%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&sdata=lmNWtN24YLhn9HHnaobAtpFEe9HSopfKVh0Hx%2BRHmKg%3D&reserved=0>
> = newName }
> }
>
> function printName(nameable) {
>   console.log(nameable.name
> <https://nam06.safelinks.protection.outlook.com/?url=http%3A%2F%2Fnameable.name%2F&data=04%7C01%7Cron.buckton%40microsoft.com%7C19322e83dccd4cbec00108d8dee98ba7%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637504442359040638%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&sdata=2zaPswdrm%2BMaP7CMjb721weL0fzrrLHoJQh1zwMX7Ao%3D&reserved=0>
> )
>   nameable.name
> <https://nam06.safelinks.protection.outlook.com/?url=http%3A%2F%2Fnameable.name%2F&data=04%7C01%7Cron.buckton%40microsoft.com%7C19322e83dccd4cbec00108d8dee98ba7%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637504442359050594%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&sdata=43nMA9tzNr%2B%2B7cZmQnKx6ZsiViEJBcPNwgsAMUIX%2F8k%3D&reserved=0> +=
> ' [printed]'
> }
> function foo(source) {
>
>   printName(new Nameable(source))
> }
> foo({ name: 'foo', type: 'pojo' })
>
> // Well defined contract way (using Typescript, but you could rely on duck
> typing if you trust the good manners of the developers)
> interface Nameable {
>   name: string;
> }
> interface Pojo extends Nameable {
>   type: string;
> }
>
> function printName(nameable: Nameable) {
>   console.log(nameable.name
> <https://nam06.safelinks.protection.outlook.com/?url=http%3A%2F%2Fnameable.name%2F&data=04%7C01%7Cron.buckton%40microsoft.com%7C19322e83dccd4cbec00108d8dee98ba7%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637504442359050594%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&sdata=43nMA9tzNr%2B%2B7cZmQnKx6ZsiViEJBcPNwgsAMUIX%2F8k%3D&reserved=0>
> )
>   nameable.name
> <https://nam06.safelinks.protection.outlook.com/?url=http%3A%2F%2Fnameable.name%2F&data=04%7C01%7Cron.buckton%40microsoft.com%7C19322e83dccd4cbec00108d8dee98ba7%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637504442359060553%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&sdata=guqE5AP50l1YXc9H2bw%2Bp91EtJRe8ou%2FRHR75EIBv6w%3D&reserved=0> +=
> ' [printed]'
>   // the function still can access the type field by ignoring the typing,
> but at this point this is the least scary thing a developer in a app
> }
> function foo(source: Pojo) {
>   printName(source)
> }
>
> // Omit and readonly way
> function printName(nameable) { /* ... */ }
> function foo(source) {
>   printName(pick(source, ['name']))
> }
> ```
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20210304/2655aec7/attachment-0001.html>


More information about the es-discuss mailing list