[EXTERNAL] Re: Destructuring by &reference
in PHP &$ref means by reference ... and in C is about the address ... I understand your proposal, but I see few gotchas in it:
let { x: ref x } = obj;
my proposal is:
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).
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:
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
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>
This was mentioned up-thread, but I wrote up this proposal several years ago (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 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
refrather than&, since&is often ascribed to unmanaged memory addresses in many languages, whileref(at least in C#) is specifically tied to references to memory managed by the GC. That proposal explainer currently includes examples forrefvariables, parameters, expressions, destructuring, and support for reifiedReferenceobjects.In userland I currently have esfx.js.org/esfx/api/ref.html as a
reflike mechanism: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 1Userland destructuring support isn't feasible, however.
In my refs proposal, you have several constructs:
refexpressions - These take a binding and create a reifiedReferenceobject from them. Examples:let rx = ref xlet rfoo = ref obj.foolet rel = ref ar[0]refdeclarations - These take a reifiedReferenceobject and create a local binding that dereferences them. Examples:let ref x2 = rx- mutable referenceconst ref foo2 = rfoo- immutable referencefunction f(ref foo) { ... }- mutable reference argumentfunction f(const ref foo) { ... }- immutable reference argumentReferenceobjects - These are runtime objects with avalueproperty:valuehas both a getter and a setter.valuehas only a getter.If the
refsyntax in my proposal were to be adopted, the above example would instead read: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 1In Augusto's example, you would have your choice of object passing or variable passing:
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
Referenceor dereferencing it is clear for some patterns:// (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 wantedHowever, this is less clear for shorthand destructuring assignments or array destructuring:
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
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<mailto:augusto.borgesm at gmail.com>> wrote:
Definitely no, but talking about the syntax additions since ES2015, they are in one or more of the categories below:
Proxy and globalThis (and the
withstatement for that matter), are mechanisms of value indirection aside from the "classic" instance propertiesHow 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?
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
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<mailto: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/eb562885/attachment-0001.html>