[Proposal] New syntax for lazy getters

classic Classic list List threaded Threaded
32 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] New syntax for lazy getters

Andrea Giammarchi-2
FWIW, I think to keep it simple `lazy: true` would be enough for the time being.

Having the new descriptor inside the descriptor seems a bit over engineering at this point, imo, but having a field already won't exclude, in the feature, the ability to improve that field (similar way addEventListener on DOM got promoted from `(type, handler, boolean)` signature to `(type, handler, boolean || options)`)

I also agree that `lazy field = expr` is a different beast, and it won't play well with descriptors as we know, but it might allow overwrites if accessed directly.

I wouldn't mind that as long as it plays well with objects and classes, and as long as there is an official way to lazy define properties, and if it could be so lazy that if redefined disappears, in that direct assignment form, it would be a solution to all my cases.

Regards


On Tue, Jun 12, 2018 at 2:37 PM, <[hidden email]> wrote:
Actually, by malleable I meant only configurable:true, so one can change it via Object.defineProp… api, I did not mean necessarily to define it as value.

I have no strong opinion on what should be there after the first access, but it boils down on how will it be exposed via Object.defineProperty, really, because as little as possible should be changed, IOW as much as possible retained.

So on case things are defined as (only pondering the property descriptor here, the call is obvious):

  { lazy: true, get: () => Math.random() } … [1]

or, bigger example:

  { lazy: { configurable: false }, enumerable: false, get: () => foos.length, set: x => console.log(`set ${x}`) } … [2]

Then what should be generated is indeed a getter so that setter may be retained as well in [2].

If the definition is:

{ lazy: { configurable: false, writable: false, enumerable: true, compute: () => Math.random() }, enumerable: false } … [3]

then it defines a value (which is not enumerable until first accessed thus created; contrived example, I know).

This post also shows a proposal how to, in future proof way, define what attributes will the replaced getter/value have: put it In lazy field of prop descriptor, lazy: true means shortcut for “the default way / Inherit from what is there now”.

Herby

On June 12, 2018 2:02:28 PM GMT+02:00, Aadit M Shah <[hidden email]> wrote:
>Okay, so my previous statement about field declarations in classes
>being
>invalid was incorrect. I didn't see Andrea's link to the class field
>declarations proposal[1]. Hence, from what I understand we're
>considering the following syntax:
>const zeros = { head: , lazy tail: this };
>
>class Random {
>    lazy value = Math.random();
>}
>
>As for semantics, Herby's philosophy of "malleable unless specified
>otherwise" makes sense. Hence, the above code would be transpiled to:
>const zeros = {
>    head: ,
>    get tail() {
>        return Object.defineProperty(this, "tail", {
>            value: this
>        }).tail;
>    }
>};
>
>class Random {
>    get value() {
>        return Object.defineProperty(this, "value", {
>            value: Math.random()
>        }).value;
>    }
>}
>
>I guess we'd also be adopting the syntax for private fields and static
>fields? For example, lazy #value and lazy static #value?
>
>On Tue, Jun 12, 2018, at 7:32 AM, [hidden email] wrote:
>>
>>
>> On June 12, 2018 11:32:22 AM GMT+02:00, Aadit M Shah
>> <[hidden email]> wrote:>> Actually, from a parsing
>perspective I believe it shouldn't be too
>>> difficult to implement the `lazy name: expression` syntax. In
>>> addition, I'm not too keen on your `lazy name() { return
>expression;>> }` syntax because:
>>> 1. It's more verbose.
>>> 2. It seems to me that it's no different than creating a regular
>>>   getter:
>>>
>>> const take = (n, xs) => n ===  ? null : xs && {    head: xs.head, 
>>> get
>>> tail() {        const value = take(n - 1, xs.tail);
>>> Object.defineProperty(this, "tail", {            configurable:
>false,>> get: () => value        });        return value;    } };
>>
>> I am pretty sure Andrea mixed syntax of lazy getter with its
>> implementation for brevity, and the actual lazy getter would
>> look like:>
>>   lazy tail() { return take(n - 1, xs.tail); }
>>
>>> Regarding the second bullet point, I've probably misunderstood
>>> what you>> were trying to convey. Perhaps you could elucidate.
>>> Anyway, making the property non-configurable after accessing it
>seems>> like a reasonable thing to do.
>>
>> Here I disagree. No syntax construct so far forces immutability. The
>> get x() / set x() ones are configurable. If you defined lazy getter
>> so far by get(), you could have changed it using
>> Object.defineProperties if there was some strange need for it. You
>> had to explicitly freeze etc. or defineProperty with configurable
>> false if you wanted to make it so.>
>> This autofreezing if the value sticks out out this philosophy of "
>> malleable unless specified otherwise".>
>>>
>>> On Tue, Jun 12, 2018, at 3:44 AM, Andrea Giammarchi wrote:
>>>> My 2 cents,
>>>> I use lazy getters since about ever and I'd love to have such
>>> syntax
>>>> in place but I think there is room for some improvement /
>>>> simplification in terms of syntax.>
>>>> *## Keep it get*ish**
>>>>
>>>> From parsing perspective, introducing `lazy tail()` seems way
>>>> simpler>>> than introducing `lazy tail:` for the simple reason that
>everything>>> that can parse `get tail()` and `set tail()` is in place
>already in>>> every engine. I don't write them but I'm sure having an
>extra
>>> keyboard
>>>> to catch shouldn't be crazy complicated.>
>>>> *## class compatible*
>>>>
>>>> because you used `delete this.tail` and mentioned functional
>>>> programming, I'd like to underline ES doesn't force anyone to one
>>>> programming style or another. That means new syntax should play
>>> nicely
>>>> with classes too, and in this case the proposal doesn't seem to
>>>> address that because of the direct value mutation, as generic
>>>> property, and the removal of that property from the object,
>>>> something>>> not needed if inherited.>
>>>> My variant would do the same, except it would keep the value an
>>>> accessor:>
>>>> ```js
>>>> const take = (n, xs) => n === 0 ? null : xs && {
>>>>   head: xs.head,
>>>>   lazy tail() {
>>>>     return Object.defineProperty(this, 'tail', {
>>>>       configurable: false,
>>>>       get: (value =>
>>>>         // still a getter
>>>>         () => value
>>>>       )(
>>>>         // executed once
>>>>         take(n - 1, xs.tail)
>>>>       )
>>>>     }).tail;
>>>>   }
>>>> };
>>>> ```
>>>>
>>>> This would keep initial accessor configuration, in terms of
>>>> enumerability, but it will freeze its value forever and, on top of
>>>> that, this will play already well with current valid ES2015
>>>> classes syntax.>
>>>> I also believe myself proposed something similar a while ago (or
>>>> somebody else and I agreed with that proposal) but for some
>>>> reason it>>> never landed.>
>>>> Hopefully this time the outcome would be different.
>>>>
>>>> Best Regards
>>>>
>>>>
>>>>
>>>>
>>>> On Tue, Jun 12, 2018 at 9:13 AM, Aadit M Shah
>>>> <[hidden email]> wrote:>> __
>>>>> Hello TC39,
>>>>>
>>>>> I recently opened an issue[1] in the tc39/ecma262[2] repository,
>>>>> proposing a new syntax for lazy getters, and I was directed to
>the>>>> CONTRIBUTING[3] page which stated that I should start a
>>>>> conversation>>>> on this mailing list.>>
>>>>> So, my feature proposal is to have syntactic sugar for
>>>>> creating lazy>>>> getters[4]. To summarize my original proposal
>(which you can
>>>>> read by>>>> following the very first link above), I find that
>creating lazy
>>>>> getters is very verbose. For example, consider:>>
>>>>> const take = (n, xs) => n ===  ? null : xs && {
>>>>>   head: xs.head,
>>>>>   get tail() {
>>>>>       delete this.tail;
>>>>>       return this.tail = take(n - 1, xs.tail);
>>>>>   }
>>>>> };
>>>>>
>>>>> My proposed solution is to add a new keyword lazy to the
>language.>>>> This keyword can only be used as a prefix to longhand
>property
>>>>> names>>>> in object initializers, and it defers the execution of
>the value
>>>>> expression until the property is accessed. In short, it's just
>>>>> syntactic sugar for lazy getters:>>
>>>>> const take = (n, xs) => n ===  ? null : xs && {
>>>>>   head: xs.head,
>>>>>   lazy tail: take(n - 1, xs.tail)
>>>>> };
>>>>>
>>>>> This is purely syntactic sugar. The semantics of this new syntax
>>>>> would remain the same as that of the desugared syntax. In
>>> particular,
>>>>> calling Object.getOwnPropertyDescriptor(list, "tail") would
>return>> an
>>>>> accessor descriptor before accessing list.tail and a data
>>>>> descriptor>>>> afterwards.>>
>>>>> Furthermore, there are other advantages of having this syntactic
>>>>> sugar. For example, creating cyclic data structures becomes much
>>>>> easier. Examples are provided in my original proposal which is
>>> linked
>>>>> above. Hope to hear your thoughts on this.>>
>>>>> Regards,
>>>>> Aadit M Shah
>>>>>
>>>>> _________________________________________________
>>>>> es-discuss mailing list
>>>>> [hidden email]
>>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>>>
>>>
>>>
>>> Links:
>>>
>>> 1. https://github.com/tc39/ecma262/issues/1223
>>> 2. https://github.com/tc39/ecma262
>>> 3. https://github.com/tc39/ecma262/blob/master/CONTRIBUTING.md
>>> 4.
>>>
>https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get#Smart_self-overwriting_lazy_getters
>
>Links:
>
>  1. https://github.com/tc39/proposal-class-fields#field-declarations


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] New syntax for lazy getters

Herby Vojčík
The problem here is that if lazy field and lazy getter are indeed
different beast, one should be able to discriminate the two inside the
property descriptor.

So maybe

   // replaces with getter on first access
   {lazy: producerFn, get: null, ...}

   // replaces with value on first access
   {lazy: producerFn, value: null, ...}

and in case both get and value are present, just be consistent with what
is the behaviour now (early error or preferences of one over the other).

Herby

Andrea Giammarchi wrote on 12. 6. 2018 14:48:

> FWIW, I think to keep it simple `lazy: true` would be enough for the
> time being.
>
> Having the new descriptor inside the descriptor seems a bit over
> engineering at this point, imo, but having a field already won't
> exclude, in the feature, the ability to improve that field (similar way
> addEventListener on DOM got promoted from `(type, handler, boolean)`
> signature to `(type, handler, boolean || options)`)
>
> I also agree that `lazy field = expr` is a different beast, and it won't
> play well with descriptors as we know, but it might allow overwrites if
> accessed directly.
>
> I wouldn't mind that as long as it plays well with objects and classes,
> and as long as there is an official way to lazy define properties, and
> if it could be so lazy that if redefined disappears, in that direct
> assignment form, it would be a solution to all my cases.
>
> Regards
>
>
> On Tue, Jun 12, 2018 at 2:37 PM, <[hidden email]
> <mailto:[hidden email]>> wrote:
>
>     Actually, by malleable I meant only configurable:true, so one can
>     change it via Object.defineProp… api, I did not mean necessarily to
>     define it as value.
>
>     I have no strong opinion on what should be there after the first
>     access, but it boils down on how will it be exposed via
>     Object.defineProperty, really, because as little as possible should
>     be changed, IOW as much as possible retained.
>
>     So on case things are defined as (only pondering the property
>     descriptor here, the call is obvious):
>
>        { lazy: true, get: () => Math.random() } … [1]
>
>     or, bigger example:
>
>        { lazy: { configurable: false }, enumerable: false, get: () =>
>     foos.length, set: x => console.log(`set ${x}`) } … [2]
>
>     Then what should be generated is indeed a getter so that setter may
>     be retained as well in [2].
>
>     If the definition is:
>
>     { lazy: { configurable: false, writable: false, enumerable: true,
>     compute: () => Math.random() }, enumerable: false } … [3]
>
>     then it defines a value (which is not enumerable until first
>     accessed thus created; contrived example, I know).
>
>     This post also shows a proposal how to, in future proof way, define
>     what attributes will the replaced getter/value have: put it In lazy
>     field of prop descriptor, lazy: true means shortcut for “the default
>     way / Inherit from what is there now”.
>
>     Herby
>
>     On June 12, 2018 2:02:28 PM GMT+02:00, Aadit M Shah
>     <[hidden email] <mailto:[hidden email]>> wrote:
>     >Okay, so my previous statement about field declarations in classes
>     >being
>     >invalid was incorrect. I didn't see Andrea's link to the class field
>      >declarations proposal[1]. Hence, from what I understand we're
>      >considering the following syntax:
>      >const zeros = { head: , lazy tail: this };
>     >
>     >class Random {
>     >    lazy value = Math.random();
>     >}
>     >
>     >As for semantics, Herby's philosophy of "malleable unless specified
>     >otherwise" makes sense. Hence, the above code would be transpiled to:
>     >const zeros = {
>      >    head: ,
>      >    get tail() {
>      >        return Object.defineProperty(this, "tail", {
>      >            value: this
>      >        }).tail;
>      >    }
>      >};
>      >
>      >class Random {
>      >    get value() {
>      >        return Object.defineProperty(this, "value", {
>      >            value: Math.random()
>      >        }).value;
>      >    }
>      >}
>      >
>      >I guess we'd also be adopting the syntax for private fields and static
>      >fields? For example, lazy #value and lazy static #value?
>      >
>      >On Tue, Jun 12, 2018, at 7:32 AM, [hidden email]
>     <mailto:[hidden email]> wrote:
>      >>
>      >>
>      >> On June 12, 2018 11:32:22 AM GMT+02:00, Aadit M Shah
>      >> <[hidden email] <mailto:[hidden email]>>
>     wrote:>> Actually, from a parsing
>      >perspective I believe it shouldn't be too
>      >>> difficult to implement the `lazy name: expression` syntax. In
>      >>> addition, I'm not too keen on your `lazy name() { return
>      >expression;>> }` syntax because:
>      >>> 1. It's more verbose.
>      >>> 2. It seems to me that it's no different than creating a regular
>      >>>   getter:
>      >>>
>      >>> const take = (n, xs) => n ===  ? null : xs && {    head: xs.head,
>      >>> get
>      >>> tail() {        const value = take(n - 1, xs.tail);
>      >>> Object.defineProperty(this, "tail", {            configurable:
>      >false,>> get: () => value        });        return value;    } };
>      >>
>      >> I am pretty sure Andrea mixed syntax of lazy getter with its
>      >> implementation for brevity, and the actual lazy getter would
>      >> look like:>
>      >>   lazy tail() { return take(n - 1, xs.tail); }
>      >>
>      >>> Regarding the second bullet point, I've probably misunderstood
>      >>> what you>> were trying to convey. Perhaps you could elucidate.
>      >>> Anyway, making the property non-configurable after accessing it
>      >seems>> like a reasonable thing to do.
>      >>
>      >> Here I disagree. No syntax construct so far forces immutability. The
>      >> get x() / set x() ones are configurable. If you defined lazy getter
>      >> so far by get(), you could have changed it using
>      >> Object.defineProperties if there was some strange need for it. You
>      >> had to explicitly freeze etc. or defineProperty with configurable
>      >> false if you wanted to make it so.>
>      >> This autofreezing if the value sticks out out this philosophy of "
>      >> malleable unless specified otherwise".>
>      >>>
>      >>> On Tue, Jun 12, 2018, at 3:44 AM, Andrea Giammarchi wrote:
>      >>>> My 2 cents,
>      >>>> I use lazy getters since about ever and I'd love to have such
>      >>> syntax
>      >>>> in place but I think there is room for some improvement /
>      >>>> simplification in terms of syntax.>
>      >>>> *## Keep it get*ish**
>      >>>>
>      >>>> From parsing perspective, introducing `lazy tail()` seems way
>      >>>> simpler>>> than introducing `lazy tail:` for the simple reason
>     that
>      >everything>>> that can parse `get tail()` and `set tail()` is in place
>      >already in>>> every engine. I don't write them but I'm sure having an
>      >extra
>      >>> keyboard
>      >>>> to catch shouldn't be crazy complicated.>
>      >>>> *## class compatible*
>      >>>>
>      >>>> because you used `delete this.tail` and mentioned functional
>      >>>> programming, I'd like to underline ES doesn't force anyone to one
>      >>>> programming style or another. That means new syntax should play
>      >>> nicely
>      >>>> with classes too, and in this case the proposal doesn't seem to
>      >>>> address that because of the direct value mutation, as generic
>      >>>> property, and the removal of that property from the object,
>      >>>> something>>> not needed if inherited.>
>      >>>> My variant would do the same, except it would keep the value an
>      >>>> accessor:>
>      >>>> ```js
>      >>>> const take = (n, xs) => n === 0 ? null : xs && {
>      >>>>   head: xs.head,
>      >>>>   lazy tail() {
>      >>>>     return Object.defineProperty(this, 'tail', {
>      >>>>       configurable: false,
>      >>>>       get: (value =>
>      >>>>         // still a getter
>      >>>>         () => value
>      >>>>       )(
>      >>>>         // executed once
>      >>>>         take(n - 1, xs.tail)
>      >>>>       )
>      >>>>     }).tail;
>      >>>>   }
>      >>>> };
>      >>>> ```
>      >>>>
>      >>>> This would keep initial accessor configuration, in terms of
>      >>>> enumerability, but it will freeze its value forever and, on top of
>      >>>> that, this will play already well with current valid ES2015
>      >>>> classes syntax.>
>      >>>> I also believe myself proposed something similar a while ago (or
>      >>>> somebody else and I agreed with that proposal) but for some
>      >>>> reason it>>> never landed.>
>      >>>> Hopefully this time the outcome would be different.
>      >>>>
>      >>>> Best Regards
>      >>>>
>      >>>>
>      >>>>
>      >>>>
>      >>>> On Tue, Jun 12, 2018 at 9:13 AM, Aadit M Shah
>      >>>> <[hidden email] <mailto:[hidden email]>>
>     wrote:>> __
>      >>>>> Hello TC39,
>      >>>>>
>      >>>>> I recently opened an issue[1] in the tc39/ecma262[2] repository,
>      >>>>> proposing a new syntax for lazy getters, and I was directed to
>      >the>>>> CONTRIBUTING[3] page which stated that I should start a
>      >>>>> conversation>>>> on this mailing list.>>
>      >>>>> So, my feature proposal is to have syntactic sugar for
>      >>>>> creating lazy>>>> getters[4]. To summarize my original proposal
>      >(which you can
>      >>>>> read by>>>> following the very first link above), I find that
>      >creating lazy
>      >>>>> getters is very verbose. For example, consider:>>
>      >>>>> const take = (n, xs) => n ===  ? null : xs && {
>      >>>>>   head: xs.head,
>      >>>>>   get tail() {
>      >>>>>       delete this.tail;
>      >>>>>       return this.tail = take(n - 1, xs.tail);
>      >>>>>   }
>      >>>>> };
>      >>>>>
>      >>>>> My proposed solution is to add a new keyword lazy to the
>      >language.>>>> This keyword can only be used as a prefix to longhand
>      >property
>      >>>>> names>>>> in object initializers, and it defers the execution of
>      >the value
>      >>>>> expression until the property is accessed. In short, it's just
>      >>>>> syntactic sugar for lazy getters:>>
>      >>>>> const take = (n, xs) => n ===  ? null : xs && {
>      >>>>>   head: xs.head,
>      >>>>>   lazy tail: take(n - 1, xs.tail)
>      >>>>> };
>      >>>>>
>      >>>>> This is purely syntactic sugar. The semantics of this new syntax
>      >>>>> would remain the same as that of the desugared syntax. In
>      >>> particular,
>      >>>>> calling Object.getOwnPropertyDescriptor(list, "tail") would
>      >return>> an
>      >>>>> accessor descriptor before accessing list.tail and a data
>      >>>>> descriptor>>>> afterwards.>>
>      >>>>> Furthermore, there are other advantages of having this syntactic
>      >>>>> sugar. For example, creating cyclic data structures becomes much
>      >>>>> easier. Examples are provided in my original proposal which is
>      >>> linked
>      >>>>> above. Hope to hear your thoughts on this.>>
>      >>>>> Regards,
>      >>>>> Aadit M Shah
>      >>>>>
>      >>>>> _________________________________________________
>     >>>>> es-discuss mailing list
>     >>>>> [hidden email] <mailto:[hidden email]>
>     >>>>> https://mail.mozilla.org/listinfo/es-discuss
>     <https://mail.mozilla.org/listinfo/es-discuss>
>     >>>>>
>     >>>
>     >>>
>     >>> Links:
>     >>>
>     >>> 1. https://github.com/tc39/ecma262/issues/1223
>     <https://github.com/tc39/ecma262/issues/1223>
>     >>> 2. https://github.com/tc39/ecma262 <https://github.com/tc39/ecma262>
>     >>> 3. https://github.com/tc39/ecma262/blob/master/CONTRIBUTING.md
>     <https://github.com/tc39/ecma262/blob/master/CONTRIBUTING.md>
>     >>> 4.
>     >>>
>     >https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get#Smart_self-overwriting_lazy_getters
>     <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get#Smart_self-overwriting_lazy_getters>
>     >
>      >Links:
>      >
>      >  1.
>     https://github.com/tc39/proposal-class-fields#field-declarations
>     <https://github.com/tc39/proposal-class-fields#field-declarations>
>
>
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] New syntax for lazy getters

Augusto Moura
I don't think a new keyword is the right path here. For classes lazy fields can easily be implemented via decorators in the last proposal[1] (also, Groovy has previous work implementing a `@Lazy` annotation[2] with AST transformations for the same use case). In code something like:
``` js
const lazy = (fieldMetadata) => {
  // change the field value in descriptor to a lazy factory
};

class Foo {

  @lazy bar = slowCalculation();

  @lazy statements = (() => {
     if (this.bar > 0) {
       return fooify();
     } else {
       return barify();
     }
  }());

}
```

What it's doesn't cover (and in my opinion should be the focus of a new proposal) is Decorators for literal objects. Something like the code below is yet not proposed:

``` js
const foo = {
  @lazy bar: 3,
};
```

I didn't made a deep search in ESDiscuss history but I remember this feature being mentioned sometime ago.

--
Augusto Moura

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] New syntax for lazy getters

Andrea Giammarchi-2
To me decorators are the answer to pretty much everything but for the last year I've seen zero progress on what seems to be a forever Stage 2 proposal: https://github.com/tc39/proposals#stage-2

Is there anything anyone can do to speed up adoption/implementation of that proposal?

On Tue, Jun 12, 2018 at 4:24 PM, Augusto Moura <[hidden email]> wrote:
I don't think a new keyword is the right path here. For classes lazy fields can easily be implemented via decorators in the last proposal[1] (also, Groovy has previous work implementing a `@Lazy` annotation[2] with AST transformations for the same use case). In code something like:
``` js
const lazy = (fieldMetadata) => {
  // change the field value in descriptor to a lazy factory
};

class Foo {

  @lazy bar = slowCalculation();

  @lazy statements = (() => {
     if (this.bar > 0) {
       return fooify();
     } else {
       return barify();
     }
  }());

}
```

What it's doesn't cover (and in my opinion should be the focus of a new proposal) is Decorators for literal objects. Something like the code below is yet not proposed:

``` js
const foo = {
  @lazy bar: 3,
};
```

I didn't made a deep search in ESDiscuss history but I remember this feature being mentioned sometime ago.

--
Augusto Moura

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss



_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] New syntax for lazy getters

Michał Wadas
Yes, something is getting done about decorators.

On Tue, Jun 12, 2018 at 4:27 PM, Andrea Giammarchi <[hidden email]> wrote:
To me decorators are the answer to pretty much everything but for the last year I've seen zero progress on what seems to be a forever Stage 2 proposal: https://github.com/tc39/proposals#stage-2

Is there anything anyone can do to speed up adoption/implementation of that proposal?

On Tue, Jun 12, 2018 at 4:24 PM, Augusto Moura <[hidden email]> wrote:
I don't think a new keyword is the right path here. For classes lazy fields can easily be implemented via decorators in the last proposal[1] (also, Groovy has previous work implementing a `@Lazy` annotation[2] with AST transformations for the same use case). In code something like:
``` js
const lazy = (fieldMetadata) => {
  // change the field value in descriptor to a lazy factory
};

class Foo {

  @lazy bar = slowCalculation();

  @lazy statements = (() => {
     if (this.bar > 0) {
       return fooify();
     } else {
       return barify();
     }
  }());

}
```

What it's doesn't cover (and in my opinion should be the focus of a new proposal) is Decorators for literal objects. Something like the code below is yet not proposed:

``` js
const foo = {
  @lazy bar: 3,
};
```

I didn't made a deep search in ESDiscuss history but I remember this feature being mentioned sometime ago.

--
Augusto Moura

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss



_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss



_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] New syntax for lazy getters

Isiah Meadows-2
In reply to this post by Andrea Giammarchi-2
BTW, I proposed similar (lazy values) 9 months ago [1], and it's been
on the list plenty of times [2]. I'd personally *love* to see it
happen, but I find it not very likely it'd make it, especially as a
property (since decorators can rectify that).

[1]: https://esdiscuss.org/topic/lazy-evaluation
[2]: https://www.google.com/search?q=site%3Aesdiscuss.org+lazy+property+OR+getter+OR+setter

-----

Isiah Meadows
[hidden email]
www.isiahmeadows.com


On Tue, Jun 12, 2018 at 8:48 AM, Andrea Giammarchi
<[hidden email]> wrote:

> FWIW, I think to keep it simple `lazy: true` would be enough for the time
> being.
>
> Having the new descriptor inside the descriptor seems a bit over engineering
> at this point, imo, but having a field already won't exclude, in the
> feature, the ability to improve that field (similar way addEventListener on
> DOM got promoted from `(type, handler, boolean)` signature to `(type,
> handler, boolean || options)`)
>
> I also agree that `lazy field = expr` is a different beast, and it won't
> play well with descriptors as we know, but it might allow overwrites if
> accessed directly.
>
> I wouldn't mind that as long as it plays well with objects and classes, and
> as long as there is an official way to lazy define properties, and if it
> could be so lazy that if redefined disappears, in that direct assignment
> form, it would be a solution to all my cases.
>
> Regards
>
>
> On Tue, Jun 12, 2018 at 2:37 PM, <[hidden email]> wrote:
>>
>> Actually, by malleable I meant only configurable:true, so one can change
>> it via Object.defineProp… api, I did not mean necessarily to define it as
>> value.
>>
>> I have no strong opinion on what should be there after the first access,
>> but it boils down on how will it be exposed via Object.defineProperty,
>> really, because as little as possible should be changed, IOW as much as
>> possible retained.
>>
>> So on case things are defined as (only pondering the property descriptor
>> here, the call is obvious):
>>
>>   { lazy: true, get: () => Math.random() } … [1]
>>
>> or, bigger example:
>>
>>   { lazy: { configurable: false }, enumerable: false, get: () =>
>> foos.length, set: x => console.log(`set ${x}`) } … [2]
>>
>> Then what should be generated is indeed a getter so that setter may be
>> retained as well in [2].
>>
>> If the definition is:
>>
>> { lazy: { configurable: false, writable: false, enumerable: true, compute:
>> () => Math.random() }, enumerable: false } … [3]
>>
>> then it defines a value (which is not enumerable until first accessed thus
>> created; contrived example, I know).
>>
>> This post also shows a proposal how to, in future proof way, define what
>> attributes will the replaced getter/value have: put it In lazy field of prop
>> descriptor, lazy: true means shortcut for “the default way / Inherit from
>> what is there now”.
>>
>> Herby
>>
>> On June 12, 2018 2:02:28 PM GMT+02:00, Aadit M Shah
>> <[hidden email]> wrote:
>> >Okay, so my previous statement about field declarations in classes
>> >being
>> >invalid was incorrect. I didn't see Andrea's link to the class field
>> >declarations proposal[1]. Hence, from what I understand we're
>> >considering the following syntax:
>> >const zeros = { head: , lazy tail: this };
>> >
>> >class Random {
>> >    lazy value = Math.random();
>> >}
>> >
>> >As for semantics, Herby's philosophy of "malleable unless specified
>> >otherwise" makes sense. Hence, the above code would be transpiled to:
>> >const zeros = {
>> >    head: ,
>> >    get tail() {
>> >        return Object.defineProperty(this, "tail", {
>> >            value: this
>> >        }).tail;
>> >    }
>> >};
>> >
>> >class Random {
>> >    get value() {
>> >        return Object.defineProperty(this, "value", {
>> >            value: Math.random()
>> >        }).value;
>> >    }
>> >}
>> >
>> >I guess we'd also be adopting the syntax for private fields and static
>> >fields? For example, lazy #value and lazy static #value?
>> >
>> >On Tue, Jun 12, 2018, at 7:32 AM, [hidden email] wrote:
>> >>
>> >>
>> >> On June 12, 2018 11:32:22 AM GMT+02:00, Aadit M Shah
>> >> <[hidden email]> wrote:>> Actually, from a parsing
>> >perspective I believe it shouldn't be too
>> >>> difficult to implement the `lazy name: expression` syntax. In
>> >>> addition, I'm not too keen on your `lazy name() { return
>> >expression;>> }` syntax because:
>> >>> 1. It's more verbose.
>> >>> 2. It seems to me that it's no different than creating a regular
>> >>>   getter:
>> >>>
>> >>> const take = (n, xs) => n ===  ? null : xs && {    head: xs.head,
>> >>> get
>> >>> tail() {        const value = take(n - 1, xs.tail);
>> >>> Object.defineProperty(this, "tail", {            configurable:
>> >false,>> get: () => value        });        return value;    } };
>> >>
>> >> I am pretty sure Andrea mixed syntax of lazy getter with its
>> >> implementation for brevity, and the actual lazy getter would
>> >> look like:>
>> >>   lazy tail() { return take(n - 1, xs.tail); }
>> >>
>> >>> Regarding the second bullet point, I've probably misunderstood
>> >>> what you>> were trying to convey. Perhaps you could elucidate.
>> >>> Anyway, making the property non-configurable after accessing it
>> >seems>> like a reasonable thing to do.
>> >>
>> >> Here I disagree. No syntax construct so far forces immutability. The
>> >> get x() / set x() ones are configurable. If you defined lazy getter
>> >> so far by get(), you could have changed it using
>> >> Object.defineProperties if there was some strange need for it. You
>> >> had to explicitly freeze etc. or defineProperty with configurable
>> >> false if you wanted to make it so.>
>> >> This autofreezing if the value sticks out out this philosophy of "
>> >> malleable unless specified otherwise".>
>> >>>
>> >>> On Tue, Jun 12, 2018, at 3:44 AM, Andrea Giammarchi wrote:
>> >>>> My 2 cents,
>> >>>> I use lazy getters since about ever and I'd love to have such
>> >>> syntax
>> >>>> in place but I think there is room for some improvement /
>> >>>> simplification in terms of syntax.>
>> >>>> *## Keep it get*ish**
>> >>>>
>> >>>> From parsing perspective, introducing `lazy tail()` seems way
>> >>>> simpler>>> than introducing `lazy tail:` for the simple reason that
>> >everything>>> that can parse `get tail()` and `set tail()` is in place
>> >already in>>> every engine. I don't write them but I'm sure having an
>> >extra
>> >>> keyboard
>> >>>> to catch shouldn't be crazy complicated.>
>> >>>> *## class compatible*
>> >>>>
>> >>>> because you used `delete this.tail` and mentioned functional
>> >>>> programming, I'd like to underline ES doesn't force anyone to one
>> >>>> programming style or another. That means new syntax should play
>> >>> nicely
>> >>>> with classes too, and in this case the proposal doesn't seem to
>> >>>> address that because of the direct value mutation, as generic
>> >>>> property, and the removal of that property from the object,
>> >>>> something>>> not needed if inherited.>
>> >>>> My variant would do the same, except it would keep the value an
>> >>>> accessor:>
>> >>>> ```js
>> >>>> const take = (n, xs) => n === 0 ? null : xs && {
>> >>>>   head: xs.head,
>> >>>>   lazy tail() {
>> >>>>     return Object.defineProperty(this, 'tail', {
>> >>>>       configurable: false,
>> >>>>       get: (value =>
>> >>>>         // still a getter
>> >>>>         () => value
>> >>>>       )(
>> >>>>         // executed once
>> >>>>         take(n - 1, xs.tail)
>> >>>>       )
>> >>>>     }).tail;
>> >>>>   }
>> >>>> };
>> >>>> ```
>> >>>>
>> >>>> This would keep initial accessor configuration, in terms of
>> >>>> enumerability, but it will freeze its value forever and, on top of
>> >>>> that, this will play already well with current valid ES2015
>> >>>> classes syntax.>
>> >>>> I also believe myself proposed something similar a while ago (or
>> >>>> somebody else and I agreed with that proposal) but for some
>> >>>> reason it>>> never landed.>
>> >>>> Hopefully this time the outcome would be different.
>> >>>>
>> >>>> Best Regards
>> >>>>
>> >>>>
>> >>>>
>> >>>>
>> >>>> On Tue, Jun 12, 2018 at 9:13 AM, Aadit M Shah
>> >>>> <[hidden email]> wrote:>> __
>> >>>>> Hello TC39,
>> >>>>>
>> >>>>> I recently opened an issue[1] in the tc39/ecma262[2] repository,
>> >>>>> proposing a new syntax for lazy getters, and I was directed to
>> >the>>>> CONTRIBUTING[3] page which stated that I should start a
>> >>>>> conversation>>>> on this mailing list.>>
>> >>>>> So, my feature proposal is to have syntactic sugar for
>> >>>>> creating lazy>>>> getters[4]. To summarize my original proposal
>> >(which you can
>> >>>>> read by>>>> following the very first link above), I find that
>> >creating lazy
>> >>>>> getters is very verbose. For example, consider:>>
>> >>>>> const take = (n, xs) => n ===  ? null : xs && {
>> >>>>>   head: xs.head,
>> >>>>>   get tail() {
>> >>>>>       delete this.tail;
>> >>>>>       return this.tail = take(n - 1, xs.tail);
>> >>>>>   }
>> >>>>> };
>> >>>>>
>> >>>>> My proposed solution is to add a new keyword lazy to the
>> >language.>>>> This keyword can only be used as a prefix to longhand
>> >property
>> >>>>> names>>>> in object initializers, and it defers the execution of
>> >the value
>> >>>>> expression until the property is accessed. In short, it's just
>> >>>>> syntactic sugar for lazy getters:>>
>> >>>>> const take = (n, xs) => n ===  ? null : xs && {
>> >>>>>   head: xs.head,
>> >>>>>   lazy tail: take(n - 1, xs.tail)
>> >>>>> };
>> >>>>>
>> >>>>> This is purely syntactic sugar. The semantics of this new syntax
>> >>>>> would remain the same as that of the desugared syntax. In
>> >>> particular,
>> >>>>> calling Object.getOwnPropertyDescriptor(list, "tail") would
>> >return>> an
>> >>>>> accessor descriptor before accessing list.tail and a data
>> >>>>> descriptor>>>> afterwards.>>
>> >>>>> Furthermore, there are other advantages of having this syntactic
>> >>>>> sugar. For example, creating cyclic data structures becomes much
>> >>>>> easier. Examples are provided in my original proposal which is
>> >>> linked
>> >>>>> above. Hope to hear your thoughts on this.>>
>> >>>>> Regards,
>> >>>>> Aadit M Shah
>> >>>>>
>> >>>>> _________________________________________________
>> >>>>> es-discuss mailing list
>> >>>>> [hidden email]
>> >>>>> https://mail.mozilla.org/listinfo/es-discuss
>> >>>>>
>> >>>
>> >>>
>> >>> Links:
>> >>>
>> >>> 1. https://github.com/tc39/ecma262/issues/1223
>> >>> 2. https://github.com/tc39/ecma262
>> >>> 3. https://github.com/tc39/ecma262/blob/master/CONTRIBUTING.md
>> >>> 4.
>> >>>
>>
>> > >https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get#Smart_self-overwriting_lazy_getters
>> >
>> >Links:
>> >
>> >  1. https://github.com/tc39/proposal-class-fields#field-declarations
>
>
>
> _______________________________________________
> es-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es-discuss
>
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] New syntax for lazy getters

kai zhu
What it's doesn't cover (and in my opinion should be the focus of a new proposal) is Decorators for literal objects. Something like the code below is yet not proposed:

``` js
const foo = {
  @lazy bar: 3,
};
```

what would happen if you tried to JSON.stringify foo? a core-value of javascript to industry is as an idiot-proof/least-surprise language for serializing json-data across browser <-> server.  junior-programmers who naively employ hard-to-serialize things like custom-getters in their low-level code, mostly end up annoying senior-programmers when they have to debug high-level integration-code that have problems baton-passing those states around.


On 12 Jun 2018, at 9:07 PM, Isiah Meadows <[hidden email]> wrote:

BTW, I proposed similar (lazy values) 9 months ago [1], and it's been
on the list plenty of times [2]. I'd personally *love* to see it
happen, but I find it not very likely it'd make it, especially as a
property (since decorators can rectify that).

[1]: https://esdiscuss.org/topic/lazy-evaluation
[2]: https://www.google.com/search?q=site%3Aesdiscuss.org+lazy+property+OR+getter+OR+setter

-----

Isiah Meadows
[hidden email]
www.isiahmeadows.com


On Tue, Jun 12, 2018 at 8:48 AM, Andrea Giammarchi
<[hidden email]> wrote:
FWIW, I think to keep it simple `lazy: true` would be enough for the time
being.

Having the new descriptor inside the descriptor seems a bit over engineering
at this point, imo, but having a field already won't exclude, in the
feature, the ability to improve that field (similar way addEventListener on
DOM got promoted from `(type, handler, boolean)` signature to `(type,
handler, boolean || options)`)

I also agree that `lazy field = expr` is a different beast, and it won't
play well with descriptors as we know, but it might allow overwrites if
accessed directly.

I wouldn't mind that as long as it plays well with objects and classes, and
as long as there is an official way to lazy define properties, and if it
could be so lazy that if redefined disappears, in that direct assignment
form, it would be a solution to all my cases.

Regards


On Tue, Jun 12, 2018 at 2:37 PM, <[hidden email]> wrote:

Actually, by malleable I meant only configurable:true, so one can change
it via Object.defineProp… api, I did not mean necessarily to define it as
value.

I have no strong opinion on what should be there after the first access,
but it boils down on how will it be exposed via Object.defineProperty,
really, because as little as possible should be changed, IOW as much as
possible retained.

So on case things are defined as (only pondering the property descriptor
here, the call is obvious):

 { lazy: true, get: () => Math.random() } … [1]

or, bigger example:

 { lazy: { configurable: false }, enumerable: false, get: () =>
foos.length, set: x => console.log(`set ${x}`) } … [2]

Then what should be generated is indeed a getter so that setter may be
retained as well in [2].

If the definition is:

{ lazy: { configurable: false, writable: false, enumerable: true, compute:
() => Math.random() }, enumerable: false } … [3]

then it defines a value (which is not enumerable until first accessed thus
created; contrived example, I know).

This post also shows a proposal how to, in future proof way, define what
attributes will the replaced getter/value have: put it In lazy field of prop
descriptor, lazy: true means shortcut for “the default way / Inherit from
what is there now”.

Herby

On June 12, 2018 2:02:28 PM GMT+02:00, Aadit M Shah
<[hidden email]> wrote:
Okay, so my previous statement about field declarations in classes
being
invalid was incorrect. I didn't see Andrea's link to the class field
declarations proposal[1]. Hence, from what I understand we're
considering the following syntax:
const zeros = { head: , lazy tail: this };

class Random {
  lazy value = Math.random();
}

As for semantics, Herby's philosophy of "malleable unless specified
otherwise" makes sense. Hence, the above code would be transpiled to:
const zeros = {
  head: ,
  get tail() {
      return Object.defineProperty(this, "tail", {
          value: this
      }).tail;
  }
};

class Random {
  get value() {
      return Object.defineProperty(this, "value", {
          value: Math.random()
      }).value;
  }
}

I guess we'd also be adopting the syntax for private fields and static
fields? For example, lazy #value and lazy static #value?

On Tue, Jun 12, 2018, at 7:32 AM, [hidden email] wrote:


On June 12, 2018 11:32:22 AM GMT+02:00, Aadit M Shah
<[hidden email]> wrote:>> Actually, from a parsing
perspective I believe it shouldn't be too
difficult to implement the `lazy name: expression` syntax. In
addition, I'm not too keen on your `lazy name() { return
expression;>> }` syntax because:
1. It's more verbose.
2. It seems to me that it's no different than creating a regular
 getter:

const take = (n, xs) => n ===  ? null : xs && {    head: xs.head,
get
tail() {        const value = take(n - 1, xs.tail);
Object.defineProperty(this, "tail", {            configurable:
false,>> get: () => value        });        return value;    } };

I am pretty sure Andrea mixed syntax of lazy getter with its
implementation for brevity, and the actual lazy getter would
look like:>
 lazy tail() { return take(n - 1, xs.tail); }

Regarding the second bullet point, I've probably misunderstood
what you>> were trying to convey. Perhaps you could elucidate.
Anyway, making the property non-configurable after accessing it
seems>> like a reasonable thing to do.

Here I disagree. No syntax construct so far forces immutability. The
get x() / set x() ones are configurable. If you defined lazy getter
so far by get(), you could have changed it using
Object.defineProperties if there was some strange need for it. You
had to explicitly freeze etc. or defineProperty with configurable
false if you wanted to make it so.>
This autofreezing if the value sticks out out this philosophy of "
malleable unless specified otherwise".>

On Tue, Jun 12, 2018, at 3:44 AM, Andrea Giammarchi wrote:
My 2 cents,
I use lazy getters since about ever and I'd love to have such
syntax
in place but I think there is room for some improvement /
simplification in terms of syntax.>
*## Keep it get*ish**

From parsing perspective, introducing `lazy tail()` seems way
simpler>>> than introducing `lazy tail:` for the simple reason that
everything>>> that can parse `get tail()` and `set tail()` is in place
already in>>> every engine. I don't write them but I'm sure having an
extra
keyboard
to catch shouldn't be crazy complicated.>
*## class compatible*

because you used `delete this.tail` and mentioned functional
programming, I'd like to underline ES doesn't force anyone to one
programming style or another. That means new syntax should play
nicely
with classes too, and in this case the proposal doesn't seem to
address that because of the direct value mutation, as generic
property, and the removal of that property from the object,
something>>> not needed if inherited.>
My variant would do the same, except it would keep the value an
accessor:>
```js
const take = (n, xs) => n === 0 ? null : xs && {
 head: xs.head,
 lazy tail() {
   return Object.defineProperty(this, 'tail', {
     configurable: false,
     get: (value =>
       // still a getter
       () => value
     )(
       // executed once
       take(n - 1, xs.tail)
     )
   }).tail;
 }
};
```

This would keep initial accessor configuration, in terms of
enumerability, but it will freeze its value forever and, on top of
that, this will play already well with current valid ES2015
classes syntax.>
I also believe myself proposed something similar a while ago (or
somebody else and I agreed with that proposal) but for some
reason it>>> never landed.>
Hopefully this time the outcome would be different.

Best Regards




On Tue, Jun 12, 2018 at 9:13 AM, Aadit M Shah
<[hidden email]> wrote:>> __
Hello TC39,

I recently opened an issue[1] in the tc39/ecma262[2] repository,
proposing a new syntax for lazy getters, and I was directed to
the>>>> CONTRIBUTING[3] page which stated that I should start a
conversation>>>> on this mailing list.>>
So, my feature proposal is to have syntactic sugar for
creating lazy>>>> getters[4]. To summarize my original proposal
(which you can
read by>>>> following the very first link above), I find that
creating lazy
getters is very verbose. For example, consider:>>
const take = (n, xs) => n ===  ? null : xs && {
 head: xs.head,
 get tail() {
     delete this.tail;
     return this.tail = take(n - 1, xs.tail);
 }
};

My proposed solution is to add a new keyword lazy to the
language.>>>> This keyword can only be used as a prefix to longhand
property
names>>>> in object initializers, and it defers the execution of
the value
expression until the property is accessed. In short, it's just
syntactic sugar for lazy getters:>>
const take = (n, xs) => n ===  ? null : xs && {
 head: xs.head,
 lazy tail: take(n - 1, xs.tail)
};

This is purely syntactic sugar. The semantics of this new syntax
would remain the same as that of the desugared syntax. In
particular,
calling Object.getOwnPropertyDescriptor(list, "tail") would
return>> an
accessor descriptor before accessing list.tail and a data
descriptor>>>> afterwards.>>
Furthermore, there are other advantages of having this syntactic
sugar. For example, creating cyclic data structures becomes much
easier. Examples are provided in my original proposal which is
linked
above. Hope to hear your thoughts on this.>>
Regards,
Aadit M Shah

_________________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss



Links:

1. https://github.com/tc39/ecma262/issues/1223
2. https://github.com/tc39/ecma262
3. https://github.com/tc39/ecma262/blob/master/CONTRIBUTING.md
4.


https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get#Smart_self-overwriting_lazy_getters

Links:

1. https://github.com/tc39/proposal-class-fields#field-declarations



_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] New syntax for lazy getters

Isiah Meadows-2
If you're wanting to propose new places for decorators, you'd have much better luck here: https://github.com/tc39/proposal-decorators

I do agree this is an obvious place for a decorator, and it is probably the ideal way of specifying it for object properties, but your larger proposal of adding decorators for objects would have better luck there.


On Wed, Jun 27, 2018 at 8:23 AM, kai zhu <[hidden email]> wrote:
What it's doesn't cover (and in my opinion should be the focus of a new proposal) is Decorators for literal objects. Something like the code below is yet not proposed:

``` js
const foo = {
  @lazy bar: 3,
};
```

what would happen if you tried to JSON.stringify foo? a core-value of javascript to industry is as an idiot-proof/least-surprise language for serializing json-data across browser <-> server.  junior-programmers who naively employ hard-to-serialize things like custom-getters in their low-level code, mostly end up annoying senior-programmers when they have to debug high-level integration-code that have problems baton-passing those states around.


On 12 Jun 2018, at 9:07 PM, Isiah Meadows <[hidden email]> wrote:

BTW, I proposed similar (lazy values) 9 months ago [1], and it's been
on the list plenty of times [2]. I'd personally *love* to see it
happen, but I find it not very likely it'd make it, especially as a
property (since decorators can rectify that).

[1]: https://esdiscuss.org/topic/lazy-evaluation
[2]: https://www.google.com/search?q=site%3Aesdiscuss.org+lazy+property+OR+getter+OR+setter

-----

Isiah Meadows
[hidden email]
www.isiahmeadows.com


On Tue, Jun 12, 2018 at 8:48 AM, Andrea Giammarchi
<[hidden email]> wrote:
FWIW, I think to keep it simple `lazy: true` would be enough for the time
being.

Having the new descriptor inside the descriptor seems a bit over engineering
at this point, imo, but having a field already won't exclude, in the
feature, the ability to improve that field (similar way addEventListener on
DOM got promoted from `(type, handler, boolean)` signature to `(type,
handler, boolean || options)`)

I also agree that `lazy field = expr` is a different beast, and it won't
play well with descriptors as we know, but it might allow overwrites if
accessed directly.

I wouldn't mind that as long as it plays well with objects and classes, and
as long as there is an official way to lazy define properties, and if it
could be so lazy that if redefined disappears, in that direct assignment
form, it would be a solution to all my cases.

Regards


On Tue, Jun 12, 2018 at 2:37 PM, <[hidden email]> wrote:

Actually, by malleable I meant only configurable:true, so one can change
it via Object.defineProp… api, I did not mean necessarily to define it as
value.

I have no strong opinion on what should be there after the first access,
but it boils down on how will it be exposed via Object.defineProperty,
really, because as little as possible should be changed, IOW as much as
possible retained.

So on case things are defined as (only pondering the property descriptor
here, the call is obvious):

 { lazy: true, get: () => Math.random() } … [1]

or, bigger example:

 { lazy: { configurable: false }, enumerable: false, get: () =>
foos.length, set: x => console.log(`set ${x}`) } … [2]

Then what should be generated is indeed a getter so that setter may be
retained as well in [2].

If the definition is:

{ lazy: { configurable: false, writable: false, enumerable: true, compute:
() => Math.random() }, enumerable: false } … [3]

then it defines a value (which is not enumerable until first accessed thus
created; contrived example, I know).

This post also shows a proposal how to, in future proof way, define what
attributes will the replaced getter/value have: put it In lazy field of prop
descriptor, lazy: true means shortcut for “the default way / Inherit from
what is there now”.

Herby

On June 12, 2018 2:02:28 PM GMT+02:00, Aadit M Shah
<[hidden email]> wrote:
Okay, so my previous statement about field declarations in classes
being
invalid was incorrect. I didn't see Andrea's link to the class field
declarations proposal[1]. Hence, from what I understand we're
considering the following syntax:
const zeros = { head: , lazy tail: this };

class Random {
  lazy value = Math.random();
}

As for semantics, Herby's philosophy of "malleable unless specified
otherwise" makes sense. Hence, the above code would be transpiled to:
const zeros = {
  head: ,
  get tail() {
      return Object.defineProperty(this, "tail", {
          value: this
      }).tail;
  }
};

class Random {
  get value() {
      return Object.defineProperty(this, "value", {
          value: Math.random()
      }).value;
  }
}

I guess we'd also be adopting the syntax for private fields and static
fields? For example, lazy #value and lazy static #value?

On Tue, Jun 12, 2018, at 7:32 AM, [hidden email] wrote:


On June 12, 2018 11:32:22 AM GMT+02:00, Aadit M Shah
<[hidden email]> wrote:>> Actually, from a parsing
perspective I believe it shouldn't be too
difficult to implement the `lazy name: expression` syntax. In
addition, I'm not too keen on your `lazy name() { return
expression;>> }` syntax because:
1. It's more verbose.
2. It seems to me that it's no different than creating a regular
 getter:

const take = (n, xs) => n ===  ? null : xs && {    head: xs.head,
get
tail() {        const value = take(n - 1, xs.tail);
Object.defineProperty(this, "tail", {            configurable:
false,>> get: () => value        });        return value;    } };

I am pretty sure Andrea mixed syntax of lazy getter with its
implementation for brevity, and the actual lazy getter would
look like:>
 lazy tail() { return take(n - 1, xs.tail); }

Regarding the second bullet point, I've probably misunderstood
what you>> were trying to convey. Perhaps you could elucidate.
Anyway, making the property non-configurable after accessing it
seems>> like a reasonable thing to do.

Here I disagree. No syntax construct so far forces immutability. The
get x() / set x() ones are configurable. If you defined lazy getter
so far by get(), you could have changed it using
Object.defineProperties if there was some strange need for it. You
had to explicitly freeze etc. or defineProperty with configurable
false if you wanted to make it so.>
This autofreezing if the value sticks out out this philosophy of "
malleable unless specified otherwise".>

On Tue, Jun 12, 2018, at 3:44 AM, Andrea Giammarchi wrote:
My 2 cents,
I use lazy getters since about ever and I'd love to have such
syntax
in place but I think there is room for some improvement /
simplification in terms of syntax.>
*## Keep it get*ish**

From parsing perspective, introducing `lazy tail()` seems way
simpler>>> than introducing `lazy tail:` for the simple reason that
everything>>> that can parse `get tail()` and `set tail()` is in place
already in>>> every engine. I don't write them but I'm sure having an
extra
keyboard
to catch shouldn't be crazy complicated.>
*## class compatible*

because you used `delete this.tail` and mentioned functional
programming, I'd like to underline ES doesn't force anyone to one
programming style or another. That means new syntax should play
nicely
with classes too, and in this case the proposal doesn't seem to
address that because of the direct value mutation, as generic
property, and the removal of that property from the object,
something>>> not needed if inherited.>
My variant would do the same, except it would keep the value an
accessor:>
```js
const take = (n, xs) => n === 0 ? null : xs && {
 head: xs.head,
 lazy tail() {
   return Object.defineProperty(this, 'tail', {
     configurable: false,
     get: (value =>
       // still a getter
       () => value
     )(
       // executed once
       take(n - 1, xs.tail)
     )
   }).tail;
 }
};
```

This would keep initial accessor configuration, in terms of
enumerability, but it will freeze its value forever and, on top of
that, this will play already well with current valid ES2015
classes syntax.>
I also believe myself proposed something similar a while ago (or
somebody else and I agreed with that proposal) but for some
reason it>>> never landed.>
Hopefully this time the outcome would be different.

Best Regards




On Tue, Jun 12, 2018 at 9:13 AM, Aadit M Shah
<[hidden email]> wrote:>> __
Hello TC39,

I recently opened an issue[1] in the tc39/ecma262[2] repository,
proposing a new syntax for lazy getters, and I was directed to
the>>>> CONTRIBUTING[3] page which stated that I should start a
conversation>>>> on this mailing list.>>
So, my feature proposal is to have syntactic sugar for
creating lazy>>>> getters[4]. To summarize my original proposal
(which you can
read by>>>> following the very first link above), I find that
creating lazy
getters is very verbose. For example, consider:>>
const take = (n, xs) => n ===  ? null : xs && {
 head: xs.head,
 get tail() {
     delete this.tail;
     return this.tail = take(n - 1, xs.tail);
 }
};

My proposed solution is to add a new keyword lazy to the
language.>>>> This keyword can only be used as a prefix to longhand
property
names>>>> in object initializers, and it defers the execution of
the value
expression until the property is accessed. In short, it's just
syntactic sugar for lazy getters:>>
const take = (n, xs) => n ===  ? null : xs && {
 head: xs.head,
 lazy tail: take(n - 1, xs.tail)
};

This is purely syntactic sugar. The semantics of this new syntax
would remain the same as that of the desugared syntax. In
particular,
calling Object.getOwnPropertyDescriptor(list, "tail") would
return>> an
accessor descriptor before accessing list.tail and a data
descriptor>>>> afterwards.>>
Furthermore, there are other advantages of having this syntactic
sugar. For example, creating cyclic data structures becomes much
easier. Examples are provided in my original proposal which is
linked
above. Hope to hear your thoughts on this.>>
Regards,
Aadit M Shah

_________________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss



Links:

1. https://github.com/tc39/ecma262/issues/1223
2. https://github.com/tc39/ecma262
3. https://github.com/tc39/ecma262/blob/master/CONTRIBUTING.md
4.


https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get#Smart_self-overwriting_lazy_getters

Links:

1. https://github.com/tc39/proposal-class-fields#field-declarations



_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss



_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] New syntax for lazy getters

Augusto Moura
In reply to this post by kai zhu


On June 27, 2018 09:23, kai zhu <[hidden email]> wrote:
> what would happen if you tried to JSON.stringify foo? a core-value of javascript to industry is as an idiot-proof/least-surprise language for serializing json-data across browser <-> server.  junior-programmers who naively employ hard-to-serialize things like custom-getters in their low-level code, mostly end up annoying senior-programmers when they have to debug high-level integration-code that have problems baton-passing those states around.

Depends on the chosen implementation (which will be open for discussion for sure). The Groovy AST transformation annotation `@Lazy` for example translates the field into a getter (without a setter, making it virtually a readonly field) which initializes the field once at the first invocation (there is a example of the generated code at Groovy docs[1]). If we follow this path we can easily implement it in Javascript as a enumerable get property descriptor. About `JSON.stringify`, it renders all enumerable properties of a object, including getters, so it will initialize the field and represent it as any other property. My previous decorator example could be loosely interpreted as:

```js
const foo = {
  get bar() {
    if (!this.hasOwnProperty('_bar')) {
      this._bar = 3;
    }
    return this._bar;
  },
};

// JSON.stringify would write the correct bar value, initalizing it if necessary
JSON.stringify(foo) // "{"bar":3}"
```

We could make a setter too, but the logic to implement it it's a lot more complex and subject to confusion

On June 27, 2018 10:11, Isiah Meadows <[hidden email]> wrote:
If you're wanting to propose new places for decorators, you'd have much better luck here: https://github.com/tc39/proposal-decorators

I think that the main proposal is already long and complex, maybe it's a better idea to finalize it first and then start a new proposal about this others decorators places (as with function expression decorators and function parameters decoratos).


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] New syntax for lazy getters

Isiah Meadows-2
I agree the main proposal is long and complex, but this kind of addition could be designed with little effort to "fall out of the grid", since it has so much in common with classes already (properties, getters/setters, methods). The main question is with properties, but those are easy to just leave out, since function calls do just as well.

Function declaration/expression/parameter decorators are completely different beasts with little in common, so I feel it's a bad comparison.

On Thu, Jun 28, 2018, 01:33 Augusto Moura <[hidden email]> wrote:
On June 27, 2018 09:23, kai zhu <[hidden email]> wrote:
> what would happen if you tried to JSON.stringify foo? a core-value of javascript to industry is as an idiot-proof/least-surprise language for serializing json-data across browser <-> server.  junior-programmers who naively employ hard-to-serialize things like custom-getters in their low-level code, mostly end up annoying senior-programmers when they have to debug high-level integration-code that have problems baton-passing those states around.

Depends on the chosen implementation (which will be open for discussion for sure). The Groovy AST transformation annotation `@Lazy` for example translates the field into a getter (without a setter, making it virtually a readonly field) which initializes the field once at the first invocation (there is a example of the generated code at Groovy docs[1]). If we follow this path we can easily implement it in Javascript as a enumerable get property descriptor. About `JSON.stringify`, it renders all enumerable properties of a object, including getters, so it will initialize the field and represent it as any other property. My previous decorator example could be loosely interpreted as:

```js
const foo = {
  get bar() {
    if (!this.hasOwnProperty('_bar')) {
      this._bar = 3;
    }
    return this._bar;
  },
};

// JSON.stringify would write the correct bar value, initalizing it if necessary
JSON.stringify(foo) // "{"bar":3}"
```

We could make a setter too, but the logic to implement it it's a lot more complex and subject to confusion

On June 27, 2018 10:11, Isiah Meadows <[hidden email]> wrote:
If you're wanting to propose new places for decorators, you'd have much better luck here: https://github.com/tc39/proposal-decorators

I think that the main proposal is already long and complex, maybe it's a better idea to finalize it first and then start a new proposal about this others decorators places (as with function expression decorators and function parameters decoratos).


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] New syntax for lazy getters

Augusto Moura
*An errata in my code*
The getter is mutating the object with a enumerable property, so
consecutives invocations of JSON.stringify will result different from
the first call (if the property is yet not initialized). The current
problem is:
```js
JSON.stringify(foo) // Returns "{"bar":3}"
// After the first bar "get" the object has now another property "_bar"
JSON.stringify(foo) // Returns "{"bar":3,"_bar":3}"
```
I did it as a quick loose scratch and didn't test it. A more robust
implementation (with a helper function) probably would use closures to
maintain the factory state. As exemplified:
```js
const defineLazyProp = (obj, propName, valueFactory) => {
  let selfDestroyingFactory = () => {
    const value = valueFactory();
    selfDestroyingFactory = () => value;
    return value;
  };

  return Object.defineProperty(obj, propName, {
    enumerable: true,
    configurable: false, // It might be discussed if the lazy prop
should be configurable or not
    get() {
      return selfDestroyingFactory();
    },
  });
};

const foo = {};

defineLazyProp(foo, 'bar', () => 3);

// This should work correctly now
console.log(JSON.stringify(foo));
console.log(JSON.stringify(foo));
```
It's a bit more verbose, but it's the best way I can think of
"ponyfilling" it at the moment.

On June 28, 02:45, Isiah Meadows <[hidden email]> wrote:
>
> I agree the main proposal is long and complex, but this kind of addition could be designed with little effort to "fall out of the grid", since it has so much in common with classes already (properties, getters/setters, methods). The main question is with properties, but those are easy to just leave out, since function calls do just as well.
>

You are right, but yet I prefer to get the classes decorators
advancing to a greater stage as soon as possible, a objet literal
decorator would be a easy extension to the current proposal. By the
way, I was comparing with the 2 others proposals by the fact that they
are more like "extensions" to the main proposal, not by the complexity
(as far as I know, mix the 3 decorators proposals in one would stall
most of the work), but yeah they really are different beasts.
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] New syntax for lazy getters

Isiah Meadows-2
FWIW, individual parts of the "grid" don't all have to ship at the
same time. Private instance fields are farther along than private
static fields, for instance, and private methods are being considered
separately from private fields. Another example is with async
functions beating async generators/iterators and generator arrow
functions being stage 1 while everything else in the function "grid"
has a generator variant. And of course, the `export foo from
"..."`/`export * as foo from "..."` proposals filling out that "grid".

And as for the decorators proposal itself, it's been sharply delayed
from having its entire data model redone multiple times over the
course of several months (according to their meeting notes). I don't
think this requires nearly as much consideration as private fields,
etc.

-----

Isiah Meadows
[hidden email]
www.isiahmeadows.com


On Thu, Jun 28, 2018 at 2:24 AM, Augusto Moura
<[hidden email]> wrote:

> *An errata in my code*
> The getter is mutating the object with a enumerable property, so
> consecutives invocations of JSON.stringify will result different from
> the first call (if the property is yet not initialized). The current
> problem is:
> ```js
> JSON.stringify(foo) // Returns "{"bar":3}"
> // After the first bar "get" the object has now another property "_bar"
> JSON.stringify(foo) // Returns "{"bar":3,"_bar":3}"
> ```
> I did it as a quick loose scratch and didn't test it. A more robust
> implementation (with a helper function) probably would use closures to
> maintain the factory state. As exemplified:
> ```js
> const defineLazyProp = (obj, propName, valueFactory) => {
>   let selfDestroyingFactory = () => {
>     const value = valueFactory();
>     selfDestroyingFactory = () => value;
>     return value;
>   };
>
>   return Object.defineProperty(obj, propName, {
>     enumerable: true,
>     configurable: false, // It might be discussed if the lazy prop
> should be configurable or not
>     get() {
>       return selfDestroyingFactory();
>     },
>   });
> };
>
> const foo = {};
>
> defineLazyProp(foo, 'bar', () => 3);
>
> // This should work correctly now
> console.log(JSON.stringify(foo));
> console.log(JSON.stringify(foo));
> ```
> It's a bit more verbose, but it's the best way I can think of
> "ponyfilling" it at the moment.
>
> On June 28, 02:45, Isiah Meadows <[hidden email]> wrote:
>>
>> I agree the main proposal is long and complex, but this kind of addition could be designed with little effort to "fall out of the grid", since it has so much in common with classes already (properties, getters/setters, methods). The main question is with properties, but those are easy to just leave out, since function calls do just as well.
>>
>
> You are right, but yet I prefer to get the classes decorators
> advancing to a greater stage as soon as possible, a objet literal
> decorator would be a easy extension to the current proposal. By the
> way, I was comparing with the 2 others proposals by the fact that they
> are more like "extensions" to the main proposal, not by the complexity
> (as far as I know, mix the 3 decorators proposals in one would stall
> most of the work), but yeah they really are different beasts.
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
12