[Proposal] New syntax for lazy getters

Aadit M Shah aaditmshah at fastmail.fm
Tue Jun 12 12:02:28 UTC 2018


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, herby at mailbox.sk wrote:
> 
> 
> On June 12, 2018 11:32:22 AM GMT+02:00, Aadit M Shah
> <aaditmshah at fastmail.fm> 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
>>> <aaditmshah at fastmail.fm> 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
>>>> es-discuss at mozilla.org
>>>> 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180612/4dd60b12/attachment-0001.html>


More information about the es-discuss mailing list