Lazy evaluation

classic Classic list List threaded Threaded
54 messages Options
123
Reply | Threaded
Open this post in threaded view
|

Re: Lazy evaluation

Michał Wadas
Why not something like decorators (not sure if decorator  proposal covers this already)? 

class Foo {
@cached
get bar() {
return something(this);

On 31 Aug 2017 10:30 pm, "Andrea Giammarchi" <[hidden email]> wrote:
it's a matter of semantics.

If I see this

```js
var later = anyWrappingName(() => Math.random());

// this is an assumption, not something obvious
later() === later()
```

If instead, I write this:
```js
this.later === this.later;
```

I expect that to never possibly fail like `arr.length === arr.length` or any `obj.prop`, in APIs with common sense, are equal to `obj.prop`.

Invokes via instances and objects? It's never obvious at first look, if that is a method execution, but it's surely a new invoke.

If you've trapped once the result behind the scene, reading that, is just noise for anyone eyes.

So, once again, are we proposing something that results into exactly this?

```js
class Later {
  get thing() {
    return Object.defineProperty(this, 'thing', {value: anyLazy()});
  }
  constructor() {
    // always true, no matter when/where
    this.thing === this.thing;
  }
}
```

If so, I'm happy. If not, this is confusing and solving not much.


Best Regards


On Thu, Aug 31, 2017 at 9:14 PM, Isiah Meadows <[hidden email]> wrote:
Yes. I'll point out that having it as a function, rather than a
property-specific thing, makes it more flexible, since you can define
constants as lazy values (I do that in quite a few places).

If you want to make it transparent, it's not that hard to make a
single-line getter/method that hides the abstraction.

Granted, most of my lazy values are properties, not constants, so I
could consider it an acceptable compromise.
-----

Isiah Meadows
[hidden email]

Looking for web consulting? Or a new website?
Send me an email and we can get started.
www.isiahmeadows.com


On Thu, Aug 31, 2017 at 3:54 PM, Andrea Giammarchi
<[hidden email]> wrote:
> so in JavaScript that results into this._db() each time, resolved lazily
> with the first value returned once ?
>
> I still think my approach is cleaner and more transparent.
>
> `get _thing() { return defineProperty(this, 'thing', value) }`
>
> but if your TS-ish stuff translates into that, works for me
>
>
>
> On Thu, Aug 31, 2017 at 8:49 PM, Isiah Meadows <[hidden email]>
> wrote:
>>
>> It takes a function, and returns a function that (if necessary)
>> initializes the value and then gets it.
>> -----
>>
>> Isiah Meadows
>> [hidden email]
>>
>> Looking for web consulting? Or a new website?
>> Send me an email and we can get started.
>> www.isiahmeadows.com
>>
>>
>> On Thu, Aug 31, 2017 at 3:43 PM, Andrea Giammarchi
>> <[hidden email]> wrote:
>> > Sorry I don't speak TS, I speak ES.
>> >
>> > Can you please tell me in JavaScript what does that do?
>> >
>> > On Thu, Aug 31, 2017 at 8:18 PM, Isiah Meadows <[hidden email]>
>> > wrote:
>> >>
>> >> Note the TS-ish declaration above it. That's the variant I was
>> >> referring to (I presented about 3 different variants initially).
>> >>
>> >> ```ts
>> >> // The declaration I included
>> >> declare function lazy<T>(init: () => T): () => T;
>> >> ```
>> >>
>> >>
>> >> On Thu, Aug 31, 2017 at 3:05 PM, Andrea Giammarchi
>> >> <[hidden email]> wrote:
>> >> > it wouldn't work, would it ? I mean, you still have to pass through
>> >> > the
>> >> > "ugly" _db.get() thingy, right?
>> >> >
>> >> > how do you access and trigger the lazy bit within the class?
>> >> >
>> >> > On Thu, Aug 31, 2017 at 7:56 PM, Isiah Meadows
>> >> > <[hidden email]>
>> >> > wrote:
>> >> >>
>> >> >> What about this (using the stage 3 class fields proposal)?
>> >> >>
>> >> >> ```js
>> >> >> declare function lazy<T>(init: () => T): () => T;
>> >> >>
>> >> >> class WithLazyVals {
>> >> >>     _db = lazy(() => new Promise(...));
>> >> >> }
>> >> >> ```
>> >> >> -----
>> >> >>
>> >> >> Isiah Meadows
>> >> >> [hidden email]
>> >> >>
>> >> >> Looking for web consulting? Or a new website?
>> >> >> Send me an email and we can get started.
>> >> >> www.isiahmeadows.com
>> >> >>
>> >> >>
>> >> >> On Thu, Aug 31, 2017 at 1:34 PM, Andrea Giammarchi
>> >> >> <[hidden email]> wrote:
>> >> >> >> this proposal doesn't compose well with classes
>> >> >> >
>> >> >> > to expand a little, if you were proposing
>> >> >> >
>> >> >> > ```js
>> >> >> > class WithLazyVals {
>> >> >> >   lazy _db() { return new Promise(...); }
>> >> >> > }
>> >> >> > ```
>> >> >> >
>> >> >> > I would've taken first flight to come over and hug you.
>> >> >> >
>> >> >> > Best Regards
>> >> >> >
>> >> >> >
>> >> >> >
>> >> >> >
>> >> >> > On Thu, Aug 31, 2017 at 6:25 PM, Andrea Giammarchi
>> >> >> > <[hidden email]> wrote:
>> >> >> >>
>> >> >> >> > How often do you start out with a class like this ...
>> >> >> >>
>> >> >> >> Never, like I've said. This is the lazy pattern I know since
>> >> >> >> ever.
>> >> >> >>
>> >> >> >> ```js
>> >> >> >> class Foo {
>> >> >> >>   get _db() {
>> >> >> >>     return Object.defineProperty(this, '_db', {
>> >> >> >>       value: new Promise((resolve, reject) => {
>> >> >> >>         // open a database connection
>> >> >> >>         // set up whatever tables you need to
>> >> >> >>         // etc.
>> >> >> >>       })
>> >> >> >>     })._db;
>> >> >> >>   }
>> >> >> >> }
>> >> >> >> ```
>> >> >> >>
>> >> >> >> Whenever you need, you just access `this._db`, no need to create
>> >> >> >> an
>> >> >> >> enumerable variable and a class method.
>> >> >> >>
>> >> >> >> It looks cleaner to me.
>> >> >> >>
>> >> >> >>
>> >> >> >> > Things you don't want to initialize right away because
>> >> >> >> > initialization
>> >> >> >>
>> >> >> >> You don't really have to convince me, I've written lazy
>> >> >> >> properties
>> >> >> >> since
>> >> >> >> getters and setters were introduced [1]
>> >> >> >>
>> >> >> >> All I am saying is that this proposal doesn't compose well with
>> >> >> >> classes,
>> >> >> >> it's just yet another SuperPrimitive for the language.
>> >> >> >>
>> >> >> >> It is also something trivial to implement on user land, yet I
>> >> >> >> haven't
>> >> >> >> seen
>> >> >> >> many writing code like the following:
>> >> >> >>
>> >> >> >> ```js
>> >> >> >> function Lazy(fn) {
>> >> >> >>   let c = false, v;
>> >> >> >>   return {get(){ return c ? v : (c = !c, v = fn()) }};
>> >> >> >> }
>> >> >> >>
>> >> >> >> var o = Lazy(() => Math.random());
>> >> >> >> o.get(); // ...
>> >> >> >> ```
>> >> >> >>
>> >> >> >> Maybe it's me that hasn't seen this widely adopted from some
>> >> >> >> library?
>> >> >> >>
>> >> >> >> Anyway, this is just my opinion, maybe others would be happy with
>> >> >> >> this.
>> >> >> >>
>> >> >> >> Best Regards
>> >> >> >>
>> >> >> >> [1] Class.lazy example
>> >> >> >>
>> >> >> >>
>> >> >> >>
>> >> >> >> https://github.com/WebReflection/prototypal/blob/master/Class.md#classlazycallback
>> >> >> >>
>> >> >> >>
>> >> >> >>
>> >> >> >> On Thu, Aug 31, 2017 at 6:03 PM, Isiah Meadows
>> >> >> >> <[hidden email]>
>> >> >> >> wrote:
>> >> >> >>>
>> >> >> >>> It'd solve a problem similarly to Kotlin's `by lazy { ... }`
>> >> >> >>> delegate,
>> >> >> >>> .NET's `System.Lazy<T>`, Swift's `lazy var`, among many other
>> >> >> >>> languages. It's very useful for lazy initialization [1], such as
>> >> >> >>> lazily setting up a database, requesting a resource, among other
>> >> >> >>> costly things. [2]
>> >> >> >>>
>> >> >> >>> How often do you start out with a class like this, where you
>> >> >> >>> have
>> >> >> >>> an
>> >> >> >>> expensive resource you don't want to open right away?
>> >> >> >>>
>> >> >> >>> ```js
>> >> >> >>> class Foo {
>> >> >> >>>     constructor() {
>> >> >> >>>         this._db = undefined
>> >> >> >>>     }
>> >> >> >>>
>> >> >> >>>     _initDb() {
>> >> >> >>>         if (this._db) return this._db
>> >> >> >>>         return this._db = new Promise((resolve, reject) => {
>> >> >> >>>             // open a database connection
>> >> >> >>>             // set up whatever tables you need to
>> >> >> >>>             // etc.
>> >> >> >>>         })
>> >> >> >>>     }
>> >> >> >>> }
>> >> >> >>> ```
>> >> >> >>>
>> >> >> >>> Or maybe, a large lookup table that takes a while to build, and
>> >> >> >>> might
>> >> >> >>> not even be used, so you don't want to do it on load?
>> >> >> >>>
>> >> >> >>> ```js
>> >> >> >>> var table
>> >> >> >>>
>> >> >> >>> function initTable() {
>> >> >> >>>     if (table) return
>> >> >> >>>     table = new Array(10000)
>> >> >> >>>     // do some expensive calculations
>> >> >> >>> }
>> >> >> >>> ```
>> >> >> >>>
>> >> >> >>> Things you don't want to initialize right away because
>> >> >> >>> initialization
>> >> >> >>> is expensive and/or the value might not even be used. That's the
>> >> >> >>> problem I'm aiming to solve, and it's something I feel would be
>> >> >> >>> useful
>> >> >> >>> in its own right in the language, about equal in importance to
>> >> >> >>> weak
>> >> >> >>> references. (Slightly specialized, but the need is not
>> >> >> >>> non-zero.)
>> >> >> >>>
>> >> >> >>> [1]: https://en.wikipedia.org/wiki/Lazy_initialization
>> >> >> >>> [2]:
>> >> >> >>>
>> >> >> >>>
>> >> >> >>>
>> >> >> >>> https://stackoverflow.com/questions/978759/what-is-lazy-initialization-and-why-is-it-useful
>> >> >> >>> -----
>> >> >> >>>
>> >> >> >>> Isiah Meadows
>> >> >> >>> [hidden email]
>> >> >> >>>
>> >> >> >>> Looking for web consulting? Or a new website?
>> >> >> >>> Send me an email and we can get started.
>> >> >> >>> www.isiahmeadows.com
>> >> >> >>>
>> >> >> >>>
>> >> >> >>> On Thu, Aug 31, 2017 at 12:23 PM, Andrea Giammarchi
>> >> >> >>> <[hidden email]> wrote:
>> >> >> >>> > right ... so ... I'm not sure I understand what this proposal
>> >> >> >>> > would
>> >> >> >>> > solve.
>> >> >> >>> >
>> >> >> >>> > Instead of this:
>> >> >> >>> > ```js
>> >> >> >>> > obj.val || (obj.val = getValue())
>> >> >> >>> > ```
>> >> >> >>> >
>> >> >> >>> > you want to do this
>> >> >> >>> > ```js
>> >> >> >>> > (obj.val || (obj.val = new Lazy(getValue)).get();
>> >> >> >>> > ```
>> >> >> >>> >
>> >> >> >>> > Where is the "win" and why is that?
>> >> >> >>> >
>> >> >> >>> >
>> >> >> >>> >
>> >> >> >>> > On Thu, Aug 31, 2017 at 5:18 PM, Isiah Meadows
>> >> >> >>> > <[hidden email]>
>> >> >> >>> > wrote:
>> >> >> >>> >>
>> >> >> >>> >> With my proposed `Lazy` class, if you were to use an instance
>> >> >> >>> >> as
>> >> >> >>> >> a
>> >> >> >>> >> descriptor, the `this` value it'd receive would not be a
>> >> >> >>> >> `Lazy`
>> >> >> >>> >> instance like it'd expect.
>> >> >> >>> >>
>> >> >> >>> >> Consider it the difference between `a.self` and `b.get()` in
>> >> >> >>> >> your
>> >> >> >>> >> example. `b.get()` is what I'd be expecting.
>> >> >> >>> >> -----
>> >> >> >>> >>
>> >> >> >>> >> Isiah Meadows
>> >> >> >>> >> [hidden email]
>> >> >> >>> >>
>> >> >> >>> >> Looking for web consulting? Or a new website?
>> >> >> >>> >> Send me an email and we can get started.
>> >> >> >>> >> www.isiahmeadows.com
>> >> >> >>> >>
>> >> >> >>> >>
>> >> >> >>> >> On Thu, Aug 31, 2017 at 12:12 PM, Andrea Giammarchi
>> >> >> >>> >> <[hidden email]> wrote:
>> >> >> >>> >> >> using it in a descriptor would get it passed the wrong
>> >> >> >>> >> >> `this`
>> >> >> >>> >> >
>> >> >> >>> >> > sorry, what?
>> >> >> >>> >> >
>> >> >> >>> >> > ```js
>> >> >> >>> >> > var a = {};
>> >> >> >>> >> > var b = {get() { return this; }};
>> >> >> >>> >> > Object.defineProperty(a, 'self', b);
>> >> >> >>> >> >
>> >> >> >>> >> > a.self === a; // true
>> >> >> >>> >> > ```
>> >> >> >>> >> >
>> >> >> >>> >> >
>> >> >> >>> >> > On Thu, Aug 31, 2017 at 5:09 PM, Isiah Meadows
>> >> >> >>> >> > <[hidden email]>
>> >> >> >>> >> > wrote:
>> >> >> >>> >> >>
>> >> >> >>> >> >> No. `Lazy` is intended to be an object to be used
>> >> >> >>> >> >> directly,
>> >> >> >>> >> >> not
>> >> >> >>> >> >> a
>> >> >> >>> >> >> descriptor of any kind.
>> >> >> >>> >> >>
>> >> >> >>> >> >> (My `lazy.get()` is an unbound method, so using it in a
>> >> >> >>> >> >> descriptor
>> >> >> >>> >> >> would get it passed the wrong `this`.)
>> >> >> >>> >> >> -----
>> >> >> >>> >> >>
>> >> >> >>> >> >> Isiah Meadows
>> >> >> >>> >> >> [hidden email]
>> >> >> >>> >> >>
>> >> >> >>> >> >> Looking for web consulting? Or a new website?
>> >> >> >>> >> >> Send me an email and we can get started.
>> >> >> >>> >> >> www.isiahmeadows.com
>> >> >> >>> >> >>
>> >> >> >>> >> >>
>> >> >> >>> >> >> On Thu, Aug 31, 2017 at 9:39 AM, Andrea Giammarchi
>> >> >> >>> >> >> <[hidden email]> wrote:
>> >> >> >>> >> >> > the following is how I usually consider lazy values
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > ```js
>> >> >> >>> >> >> > class Any {
>> >> >> >>> >> >> >   _lazy(name) {
>> >> >> >>> >> >> >     switch (name) {
>> >> >> >>> >> >> >       case 'uid': return Math.random();
>> >> >> >>> >> >> >       // others ... eventually
>> >> >> >>> >> >> >     }
>> >> >> >>> >> >> >   }
>> >> >> >>> >> >> >   get uid() {
>> >> >> >>> >> >> >     var value = this._lazy('uid');
>> >> >> >>> >> >> >     // from now on, direct access
>> >> >> >>> >> >> >     Object.defineProperty(this, 'uid', {value});
>> >> >> >>> >> >> >     return value;
>> >> >> >>> >> >> >   }
>> >> >> >>> >> >> > }
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > const a = new Any;
>> >> >> >>> >> >> > a.uid === a.uid; // true
>> >> >> >>> >> >> > ```
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > If I understand correctly your proposal is to use Lazy
>> >> >> >>> >> >> > as
>> >> >> >>> >> >> > generic
>> >> >> >>> >> >> > descriptor, is that correct ?
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > ```js
>> >> >> >>> >> >> > Object.defineProperty({}, 'something', new Lazy(function
>> >> >> >>> >> >> > (val)
>> >> >> >>> >> >> > {
>> >> >> >>> >> >> >   return this.shakaLaka ? val : 'no shakaLaka';
>> >> >> >>> >> >> > }));
>> >> >> >>> >> >> > ```
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > ???
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > If that's the case I see already people confused by
>> >> >> >>> >> >> > arrow
>> >> >> >>> >> >> > function
>> >> >> >>> >> >> > in case they need to access the context,
>> >> >> >>> >> >> > plus no property access optimization once resolved.
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > It's also not clear if such property can be set again
>> >> >> >>> >> >> > later
>> >> >> >>> >> >> > on
>> >> >> >>> >> >> > (right
>> >> >> >>> >> >> > now it
>> >> >> >>> >> >> > cannot)
>> >> >> >>> >> >> > 'cause lazy definition doesn't always necessarily mean
>> >> >> >>> >> >> > inability
>> >> >> >>> >> >> > to
>> >> >> >>> >> >> > reassign.
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > What am I missing/misunderstanding?
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > Regards
>> >> >> >>> >> >> >
>> >> >> >>> >> >> >
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > On Thu, Aug 31, 2017 at 2:21 PM, Isiah Meadows
>> >> >> >>> >> >> > <[hidden email]>
>> >> >> >>> >> >> > wrote:
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> It'd be really nice if lazy values made it into the
>> >> >> >>> >> >> >> spec
>> >> >> >>> >> >> >> somehow.
>> >> >> >>> >> >> >> I've
>> >> >> >>> >> >> >> already found myself using things like this [1] quite a
>> >> >> >>> >> >> >> bit,
>> >> >> >>> >> >> >> and
>> >> >> >>> >> >> >> I've
>> >> >> >>> >> >> >> also found myself frequently initializing properties
>> >> >> >>> >> >> >> not
>> >> >> >>> >> >> >> on
>> >> >> >>> >> >> >> first
>> >> >> >>> >> >> >> access.
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> [1]:
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> https://gist.github.com/isiahmeadows/4c0723bdfa555a1c2cb01341b323c3d4
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> As for what would be a nice API, maybe something like
>> >> >> >>> >> >> >> one
>> >> >> >>> >> >> >> of
>> >> >> >>> >> >> >> these?
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> ```js
>> >> >> >>> >> >> >> class Lazy<T> {
>> >> >> >>> >> >> >>     constructor(init: () => T);
>> >> >> >>> >> >> >>     get(): T; // or error thrown
>> >> >> >>> >> >> >> }
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> function lazy<T>(init: () => T): () => T; // or error
>> >> >> >>> >> >> >> thrown
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> function lazy<T>(init: () => T): {
>> >> >> >>> >> >> >>     get(): T; // or error thrown
>> >> >> >>> >> >> >> }
>> >> >> >>> >> >> >> ```
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> Alternatively, syntax might work, with `do` expression
>> >> >> >>> >> >> >> semantics:
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> ```js
>> >> >> >>> >> >> >> const x = lazy do { ... }
>> >> >> >>> >> >> >> // expose via `x.get()` or just `x()`
>> >> >> >>> >> >> >> ```
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> -----
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> Isiah Meadows
>> >> >> >>> >> >> >> [hidden email]
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> Looking for web consulting? Or a new website?
>> >> >> >>> >> >> >> Send me an email and we can get started.
>> >> >> >>> >> >> >> www.isiahmeadows.com
>> >> >> >>> >> >> >> _______________________________________________
>> >> >> >>> >> >> >> es-discuss mailing list
>> >> >> >>> >> >> >> [hidden email]
>> >> >> >>> >> >> >> https://mail.mozilla.org/listinfo/es-discuss
>> >> >> >>> >> >> >
>> >> >> >>> >> >> >
>> >> >> >>> >> >
>> >> >> >>> >> >
>> >> >> >>> >
>> >> >> >>> >
>> >> >> >>
>> >> >> >>
>> >> >> >
>> >> >
>> >> >
>> >>
>> >> -----
>> >>
>> >> Isiah Meadows
>> >> [hidden email]
>> >>
>> >> Looking for web consulting? Or a new website?
>> >> Send me an email and we can get started.
>> >> www.isiahmeadows.com
>> >
>> >
>
>


_______________________________________________
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: Lazy evaluation

Alexander Jones
I'm surprised no-one has mentioned memoization yet.

A lazy constant can be modelled with memoization of a nullary function. Memoization only works when you have defined a concept of *value* for the argument pack (to act as the cache key), which IMO is probably best dealt with by something like Immutable.js. The 0 argument "constant" case is trivial and need not invoke something like Immutable, but I'd rather try to consider approaching something a little more general than just "lazy values".

```js
const memoize = fn => /* some definition based on Immutable values as function arguments */;
const expensiveToRetrieve = memoize(() => whatever);
const fastestRoute = memoize((pointA, pointB) => whatever);
```

With freestanding "getters" you could even have these resembling normal variables without the need for `()`. Not sure where we stand on that though.

Alex

On 31 August 2017 at 21:53, Michał Wadas <[hidden email]> wrote:
Why not something like decorators (not sure if decorator  proposal covers this already)? 

class Foo {
@cached
get bar() {
return something(this);

On 31 Aug 2017 10:30 pm, "Andrea Giammarchi" <[hidden email]> wrote:
it's a matter of semantics.

If I see this

```js
var later = anyWrappingName(() => Math.random());

// this is an assumption, not something obvious
later() === later()
```

If instead, I write this:
```js
this.later === this.later;
```

I expect that to never possibly fail like `arr.length === arr.length` or any `obj.prop`, in APIs with common sense, are equal to `obj.prop`.

Invokes via instances and objects? It's never obvious at first look, if that is a method execution, but it's surely a new invoke.

If you've trapped once the result behind the scene, reading that, is just noise for anyone eyes.

So, once again, are we proposing something that results into exactly this?

```js
class Later {
  get thing() {
    return Object.defineProperty(this, 'thing', {value: anyLazy()});
  }
  constructor() {
    // always true, no matter when/where
    this.thing === this.thing;
  }
}
```

If so, I'm happy. If not, this is confusing and solving not much.


Best Regards


On Thu, Aug 31, 2017 at 9:14 PM, Isiah Meadows <[hidden email]> wrote:
Yes. I'll point out that having it as a function, rather than a
property-specific thing, makes it more flexible, since you can define
constants as lazy values (I do that in quite a few places).

If you want to make it transparent, it's not that hard to make a
single-line getter/method that hides the abstraction.

Granted, most of my lazy values are properties, not constants, so I
could consider it an acceptable compromise.
-----

Isiah Meadows
[hidden email]

Looking for web consulting? Or a new website?
Send me an email and we can get started.
www.isiahmeadows.com


On Thu, Aug 31, 2017 at 3:54 PM, Andrea Giammarchi
<[hidden email]> wrote:
> so in JavaScript that results into this._db() each time, resolved lazily
> with the first value returned once ?
>
> I still think my approach is cleaner and more transparent.
>
> `get _thing() { return defineProperty(this, 'thing', value) }`
>
> but if your TS-ish stuff translates into that, works for me
>
>
>
> On Thu, Aug 31, 2017 at 8:49 PM, Isiah Meadows <[hidden email]>
> wrote:
>>
>> It takes a function, and returns a function that (if necessary)
>> initializes the value and then gets it.
>> -----
>>
>> Isiah Meadows
>> [hidden email]
>>
>> Looking for web consulting? Or a new website?
>> Send me an email and we can get started.
>> www.isiahmeadows.com
>>
>>
>> On Thu, Aug 31, 2017 at 3:43 PM, Andrea Giammarchi
>> <[hidden email]> wrote:
>> > Sorry I don't speak TS, I speak ES.
>> >
>> > Can you please tell me in JavaScript what does that do?
>> >
>> > On Thu, Aug 31, 2017 at 8:18 PM, Isiah Meadows <[hidden email]>
>> > wrote:
>> >>
>> >> Note the TS-ish declaration above it. That's the variant I was
>> >> referring to (I presented about 3 different variants initially).
>> >>
>> >> ```ts
>> >> // The declaration I included
>> >> declare function lazy<T>(init: () => T): () => T;
>> >> ```
>> >>
>> >>
>> >> On Thu, Aug 31, 2017 at 3:05 PM, Andrea Giammarchi
>> >> <[hidden email]> wrote:
>> >> > it wouldn't work, would it ? I mean, you still have to pass through
>> >> > the
>> >> > "ugly" _db.get() thingy, right?
>> >> >
>> >> > how do you access and trigger the lazy bit within the class?
>> >> >
>> >> > On Thu, Aug 31, 2017 at 7:56 PM, Isiah Meadows
>> >> > <[hidden email]>
>> >> > wrote:
>> >> >>
>> >> >> What about this (using the stage 3 class fields proposal)?
>> >> >>
>> >> >> ```js
>> >> >> declare function lazy<T>(init: () => T): () => T;
>> >> >>
>> >> >> class WithLazyVals {
>> >> >>     _db = lazy(() => new Promise(...));
>> >> >> }
>> >> >> ```
>> >> >> -----
>> >> >>
>> >> >> Isiah Meadows
>> >> >> [hidden email]
>> >> >>
>> >> >> Looking for web consulting? Or a new website?
>> >> >> Send me an email and we can get started.
>> >> >> www.isiahmeadows.com
>> >> >>
>> >> >>
>> >> >> On Thu, Aug 31, 2017 at 1:34 PM, Andrea Giammarchi
>> >> >> <[hidden email]> wrote:
>> >> >> >> this proposal doesn't compose well with classes
>> >> >> >
>> >> >> > to expand a little, if you were proposing
>> >> >> >
>> >> >> > ```js
>> >> >> > class WithLazyVals {
>> >> >> >   lazy _db() { return new Promise(...); }
>> >> >> > }
>> >> >> > ```
>> >> >> >
>> >> >> > I would've taken first flight to come over and hug you.
>> >> >> >
>> >> >> > Best Regards
>> >> >> >
>> >> >> >
>> >> >> >
>> >> >> >
>> >> >> > On Thu, Aug 31, 2017 at 6:25 PM, Andrea Giammarchi
>> >> >> > <[hidden email]> wrote:
>> >> >> >>
>> >> >> >> > How often do you start out with a class like this ...
>> >> >> >>
>> >> >> >> Never, like I've said. This is the lazy pattern I know since
>> >> >> >> ever.
>> >> >> >>
>> >> >> >> ```js
>> >> >> >> class Foo {
>> >> >> >>   get _db() {
>> >> >> >>     return Object.defineProperty(this, '_db', {
>> >> >> >>       value: new Promise((resolve, reject) => {
>> >> >> >>         // open a database connection
>> >> >> >>         // set up whatever tables you need to
>> >> >> >>         // etc.
>> >> >> >>       })
>> >> >> >>     })._db;
>> >> >> >>   }
>> >> >> >> }
>> >> >> >> ```
>> >> >> >>
>> >> >> >> Whenever you need, you just access `this._db`, no need to create
>> >> >> >> an
>> >> >> >> enumerable variable and a class method.
>> >> >> >>
>> >> >> >> It looks cleaner to me.
>> >> >> >>
>> >> >> >>
>> >> >> >> > Things you don't want to initialize right away because
>> >> >> >> > initialization
>> >> >> >>
>> >> >> >> You don't really have to convince me, I've written lazy
>> >> >> >> properties
>> >> >> >> since
>> >> >> >> getters and setters were introduced [1]
>> >> >> >>
>> >> >> >> All I am saying is that this proposal doesn't compose well with
>> >> >> >> classes,
>> >> >> >> it's just yet another SuperPrimitive for the language.
>> >> >> >>
>> >> >> >> It is also something trivial to implement on user land, yet I
>> >> >> >> haven't
>> >> >> >> seen
>> >> >> >> many writing code like the following:
>> >> >> >>
>> >> >> >> ```js
>> >> >> >> function Lazy(fn) {
>> >> >> >>   let c = false, v;
>> >> >> >>   return {get(){ return c ? v : (c = !c, v = fn()) }};
>> >> >> >> }
>> >> >> >>
>> >> >> >> var o = Lazy(() => Math.random());
>> >> >> >> o.get(); // ...
>> >> >> >> ```
>> >> >> >>
>> >> >> >> Maybe it's me that hasn't seen this widely adopted from some
>> >> >> >> library?
>> >> >> >>
>> >> >> >> Anyway, this is just my opinion, maybe others would be happy with
>> >> >> >> this.
>> >> >> >>
>> >> >> >> Best Regards
>> >> >> >>
>> >> >> >> [1] Class.lazy example
>> >> >> >>
>> >> >> >>
>> >> >> >>
>> >> >> >> https://github.com/WebReflection/prototypal/blob/master/Class.md#classlazycallback
>> >> >> >>
>> >> >> >>
>> >> >> >>
>> >> >> >> On Thu, Aug 31, 2017 at 6:03 PM, Isiah Meadows
>> >> >> >> <[hidden email]>
>> >> >> >> wrote:
>> >> >> >>>
>> >> >> >>> It'd solve a problem similarly to Kotlin's `by lazy { ... }`
>> >> >> >>> delegate,
>> >> >> >>> .NET's `System.Lazy<T>`, Swift's `lazy var`, among many other
>> >> >> >>> languages. It's very useful for lazy initialization [1], such as
>> >> >> >>> lazily setting up a database, requesting a resource, among other
>> >> >> >>> costly things. [2]
>> >> >> >>>
>> >> >> >>> How often do you start out with a class like this, where you
>> >> >> >>> have
>> >> >> >>> an
>> >> >> >>> expensive resource you don't want to open right away?
>> >> >> >>>
>> >> >> >>> ```js
>> >> >> >>> class Foo {
>> >> >> >>>     constructor() {
>> >> >> >>>         this._db = undefined
>> >> >> >>>     }
>> >> >> >>>
>> >> >> >>>     _initDb() {
>> >> >> >>>         if (this._db) return this._db
>> >> >> >>>         return this._db = new Promise((resolve, reject) => {
>> >> >> >>>             // open a database connection
>> >> >> >>>             // set up whatever tables you need to
>> >> >> >>>             // etc.
>> >> >> >>>         })
>> >> >> >>>     }
>> >> >> >>> }
>> >> >> >>> ```
>> >> >> >>>
>> >> >> >>> Or maybe, a large lookup table that takes a while to build, and
>> >> >> >>> might
>> >> >> >>> not even be used, so you don't want to do it on load?
>> >> >> >>>
>> >> >> >>> ```js
>> >> >> >>> var table
>> >> >> >>>
>> >> >> >>> function initTable() {
>> >> >> >>>     if (table) return
>> >> >> >>>     table = new Array(10000)
>> >> >> >>>     // do some expensive calculations
>> >> >> >>> }
>> >> >> >>> ```
>> >> >> >>>
>> >> >> >>> Things you don't want to initialize right away because
>> >> >> >>> initialization
>> >> >> >>> is expensive and/or the value might not even be used. That's the
>> >> >> >>> problem I'm aiming to solve, and it's something I feel would be
>> >> >> >>> useful
>> >> >> >>> in its own right in the language, about equal in importance to
>> >> >> >>> weak
>> >> >> >>> references. (Slightly specialized, but the need is not
>> >> >> >>> non-zero.)
>> >> >> >>>
>> >> >> >>> [1]: https://en.wikipedia.org/wiki/Lazy_initialization
>> >> >> >>> [2]:
>> >> >> >>>
>> >> >> >>>
>> >> >> >>>
>> >> >> >>> https://stackoverflow.com/questions/978759/what-is-lazy-initialization-and-why-is-it-useful
>> >> >> >>> -----
>> >> >> >>>
>> >> >> >>> Isiah Meadows
>> >> >> >>> [hidden email]
>> >> >> >>>
>> >> >> >>> Looking for web consulting? Or a new website?
>> >> >> >>> Send me an email and we can get started.
>> >> >> >>> www.isiahmeadows.com
>> >> >> >>>
>> >> >> >>>
>> >> >> >>> On Thu, Aug 31, 2017 at 12:23 PM, Andrea Giammarchi
>> >> >> >>> <[hidden email]> wrote:
>> >> >> >>> > right ... so ... I'm not sure I understand what this proposal
>> >> >> >>> > would
>> >> >> >>> > solve.
>> >> >> >>> >
>> >> >> >>> > Instead of this:
>> >> >> >>> > ```js
>> >> >> >>> > obj.val || (obj.val = getValue())
>> >> >> >>> > ```
>> >> >> >>> >
>> >> >> >>> > you want to do this
>> >> >> >>> > ```js
>> >> >> >>> > (obj.val || (obj.val = new Lazy(getValue)).get();
>> >> >> >>> > ```
>> >> >> >>> >
>> >> >> >>> > Where is the "win" and why is that?
>> >> >> >>> >
>> >> >> >>> >
>> >> >> >>> >
>> >> >> >>> > On Thu, Aug 31, 2017 at 5:18 PM, Isiah Meadows
>> >> >> >>> > <[hidden email]>
>> >> >> >>> > wrote:
>> >> >> >>> >>
>> >> >> >>> >> With my proposed `Lazy` class, if you were to use an instance
>> >> >> >>> >> as
>> >> >> >>> >> a
>> >> >> >>> >> descriptor, the `this` value it'd receive would not be a
>> >> >> >>> >> `Lazy`
>> >> >> >>> >> instance like it'd expect.
>> >> >> >>> >>
>> >> >> >>> >> Consider it the difference between `a.self` and `b.get()` in
>> >> >> >>> >> your
>> >> >> >>> >> example. `b.get()` is what I'd be expecting.
>> >> >> >>> >> -----
>> >> >> >>> >>
>> >> >> >>> >> Isiah Meadows
>> >> >> >>> >> [hidden email]
>> >> >> >>> >>
>> >> >> >>> >> Looking for web consulting? Or a new website?
>> >> >> >>> >> Send me an email and we can get started.
>> >> >> >>> >> www.isiahmeadows.com
>> >> >> >>> >>
>> >> >> >>> >>
>> >> >> >>> >> On Thu, Aug 31, 2017 at 12:12 PM, Andrea Giammarchi
>> >> >> >>> >> <[hidden email]> wrote:
>> >> >> >>> >> >> using it in a descriptor would get it passed the wrong
>> >> >> >>> >> >> `this`
>> >> >> >>> >> >
>> >> >> >>> >> > sorry, what?
>> >> >> >>> >> >
>> >> >> >>> >> > ```js
>> >> >> >>> >> > var a = {};
>> >> >> >>> >> > var b = {get() { return this; }};
>> >> >> >>> >> > Object.defineProperty(a, 'self', b);
>> >> >> >>> >> >
>> >> >> >>> >> > a.self === a; // true
>> >> >> >>> >> > ```
>> >> >> >>> >> >
>> >> >> >>> >> >
>> >> >> >>> >> > On Thu, Aug 31, 2017 at 5:09 PM, Isiah Meadows
>> >> >> >>> >> > <[hidden email]>
>> >> >> >>> >> > wrote:
>> >> >> >>> >> >>
>> >> >> >>> >> >> No. `Lazy` is intended to be an object to be used
>> >> >> >>> >> >> directly,
>> >> >> >>> >> >> not
>> >> >> >>> >> >> a
>> >> >> >>> >> >> descriptor of any kind.
>> >> >> >>> >> >>
>> >> >> >>> >> >> (My `lazy.get()` is an unbound method, so using it in a
>> >> >> >>> >> >> descriptor
>> >> >> >>> >> >> would get it passed the wrong `this`.)
>> >> >> >>> >> >> -----
>> >> >> >>> >> >>
>> >> >> >>> >> >> Isiah Meadows
>> >> >> >>> >> >> [hidden email]
>> >> >> >>> >> >>
>> >> >> >>> >> >> Looking for web consulting? Or a new website?
>> >> >> >>> >> >> Send me an email and we can get started.
>> >> >> >>> >> >> www.isiahmeadows.com
>> >> >> >>> >> >>
>> >> >> >>> >> >>
>> >> >> >>> >> >> On Thu, Aug 31, 2017 at 9:39 AM, Andrea Giammarchi
>> >> >> >>> >> >> <[hidden email]> wrote:
>> >> >> >>> >> >> > the following is how I usually consider lazy values
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > ```js
>> >> >> >>> >> >> > class Any {
>> >> >> >>> >> >> >   _lazy(name) {
>> >> >> >>> >> >> >     switch (name) {
>> >> >> >>> >> >> >       case 'uid': return Math.random();
>> >> >> >>> >> >> >       // others ... eventually
>> >> >> >>> >> >> >     }
>> >> >> >>> >> >> >   }
>> >> >> >>> >> >> >   get uid() {
>> >> >> >>> >> >> >     var value = this._lazy('uid');
>> >> >> >>> >> >> >     // from now on, direct access
>> >> >> >>> >> >> >     Object.defineProperty(this, 'uid', {value});
>> >> >> >>> >> >> >     return value;
>> >> >> >>> >> >> >   }
>> >> >> >>> >> >> > }
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > const a = new Any;
>> >> >> >>> >> >> > a.uid === a.uid; // true
>> >> >> >>> >> >> > ```
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > If I understand correctly your proposal is to use Lazy
>> >> >> >>> >> >> > as
>> >> >> >>> >> >> > generic
>> >> >> >>> >> >> > descriptor, is that correct ?
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > ```js
>> >> >> >>> >> >> > Object.defineProperty({}, 'something', new Lazy(function
>> >> >> >>> >> >> > (val)
>> >> >> >>> >> >> > {
>> >> >> >>> >> >> >   return this.shakaLaka ? val : 'no shakaLaka';
>> >> >> >>> >> >> > }));
>> >> >> >>> >> >> > ```
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > ???
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > If that's the case I see already people confused by
>> >> >> >>> >> >> > arrow
>> >> >> >>> >> >> > function
>> >> >> >>> >> >> > in case they need to access the context,
>> >> >> >>> >> >> > plus no property access optimization once resolved.
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > It's also not clear if such property can be set again
>> >> >> >>> >> >> > later
>> >> >> >>> >> >> > on
>> >> >> >>> >> >> > (right
>> >> >> >>> >> >> > now it
>> >> >> >>> >> >> > cannot)
>> >> >> >>> >> >> > 'cause lazy definition doesn't always necessarily mean
>> >> >> >>> >> >> > inability
>> >> >> >>> >> >> > to
>> >> >> >>> >> >> > reassign.
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > What am I missing/misunderstanding?
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > Regards
>> >> >> >>> >> >> >
>> >> >> >>> >> >> >
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > On Thu, Aug 31, 2017 at 2:21 PM, Isiah Meadows
>> >> >> >>> >> >> > <[hidden email]>
>> >> >> >>> >> >> > wrote:
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> It'd be really nice if lazy values made it into the
>> >> >> >>> >> >> >> spec
>> >> >> >>> >> >> >> somehow.
>> >> >> >>> >> >> >> I've
>> >> >> >>> >> >> >> already found myself using things like this [1] quite a
>> >> >> >>> >> >> >> bit,
>> >> >> >>> >> >> >> and
>> >> >> >>> >> >> >> I've
>> >> >> >>> >> >> >> also found myself frequently initializing properties
>> >> >> >>> >> >> >> not
>> >> >> >>> >> >> >> on
>> >> >> >>> >> >> >> first
>> >> >> >>> >> >> >> access.
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> [1]:
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> https://gist.github.com/isiahmeadows/4c0723bdfa555a1c2cb01341b323c3d4
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> As for what would be a nice API, maybe something like
>> >> >> >>> >> >> >> one
>> >> >> >>> >> >> >> of
>> >> >> >>> >> >> >> these?
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> ```js
>> >> >> >>> >> >> >> class Lazy<T> {
>> >> >> >>> >> >> >>     constructor(init: () => T);
>> >> >> >>> >> >> >>     get(): T; // or error thrown
>> >> >> >>> >> >> >> }
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> function lazy<T>(init: () => T): () => T; // or error
>> >> >> >>> >> >> >> thrown
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> function lazy<T>(init: () => T): {
>> >> >> >>> >> >> >>     get(): T; // or error thrown
>> >> >> >>> >> >> >> }
>> >> >> >>> >> >> >> ```
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> Alternatively, syntax might work, with `do` expression
>> >> >> >>> >> >> >> semantics:
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> ```js
>> >> >> >>> >> >> >> const x = lazy do { ... }
>> >> >> >>> >> >> >> // expose via `x.get()` or just `x()`
>> >> >> >>> >> >> >> ```
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> -----
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> Isiah Meadows
>> >> >> >>> >> >> >> [hidden email]
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> Looking for web consulting? Or a new website?
>> >> >> >>> >> >> >> Send me an email and we can get started.
>> >> >> >>> >> >> >> www.isiahmeadows.com
>> >> >> >>> >> >> >> _______________________________________________
>> >> >> >>> >> >> >> es-discuss mailing list
>> >> >> >>> >> >> >> [hidden email]
>> >> >> >>> >> >> >> https://mail.mozilla.org/listinfo/es-discuss
>> >> >> >>> >> >> >
>> >> >> >>> >> >> >
>> >> >> >>> >> >
>> >> >> >>> >> >
>> >> >> >>> >
>> >> >> >>> >
>> >> >> >>
>> >> >> >>
>> >> >> >
>> >> >
>> >> >
>> >>
>> >> -----
>> >>
>> >> Isiah Meadows
>> >> [hidden email]
>> >>
>> >> Looking for web consulting? Or a new website?
>> >> Send me an email and we can get started.
>> >> www.isiahmeadows.com
>> >
>> >
>
>


_______________________________________________
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: Lazy evaluation

kai zhu
In reply to this post by Isiah Meadows-2
inline

On Sep 1, 2017, at 1:03 AM, Isiah Meadows <[hidden email]> wrote:

It'd solve a problem similarly to Kotlin's `by lazy { ... }` delegate,
.NET's `System.Lazy<T>`, Swift's `lazy var`, among many other
languages. It's very useful for lazy initialization [1], such as
lazily setting up a database, requesting a resource, among other
costly things. [2]

How often do you start out with a class like this, where you have an
expensive resource you don't want to open right away?

```js
class Foo {
   constructor() {
       this._db = undefined
   }

   _initDb() {
       if (this._db) return this._db
       return this._db = new Promise((resolve, reject) => {
           // open a database connection
           // set up whatever tables you need to
           // etc.
       })
   }
}
```

lazy db-initialization is over-engineering and unnecessary.  almost all applications i encounter can be designed more simply with explicit db-initialization during startup.  the only problem that arises is when the user tries to access the db before db-initialization completes.  my solution (for indexeddb) is to wrap every db-crud method with a deferred-callback that waits for db-initialization to complete (or a promise object as andreas mentioned).

storageGetItem = function (key, onError) {
/*
 * this function will get the item with the given key from storage
 */
    defer({ action: 'getItem', key: key }, onError);
}
storageRemoveItem = function (key, onError) {
/*
 * this function will remove the item with the given key from storage
 */
    defer({ action: 'removeItem', key: key }, onError);
}
storageSetItem = function (key, value, onError) {
/*
 * this function will set the item with the given key and value to storage
 */
    defer({ action: 'setItem', key: key, value: value }, onError);
}
storageDefer = function (options, onError) {
/*
 * this function will defer options.action until storage is ready
 */
    var data, isDone, objectStore, onError2, request, tmp;
    onError = onError || function (error) {
        // validate no error occurred
        console.assert(!error, error);
    };
    if (!storage) {
        deferList.push(function () {
            defer(options, onError);
        });
        init();
        return;
    }
    switch (modeJs) {
    case 'browser':
        onError2 = function () {
            /* istanbul ignore next */
            if (isDone) {
                return;
            }
            isDone = true;
            onError(
                request && (request.error || request.transaction.error),
                data || request.result || ''
            );
        };
        switch (options.action) {
        case 'clear':
        case 'removeItem':
        case 'setItem':
            objectStore = storage
                .transaction(storageDir, 'readwrite')
                .objectStore(storageDir);
            break;
        default:
            objectStore = storage
                .transaction(storageDir, 'readonly')
                .objectStore(storageDir);
        }
        switch (options.action) {
        case 'clear':
            request = objectStore.clear();
            break;
        case 'getItem':
            request = objectStore.get(String(options.key));
            break;
        case 'keys':
            data = [];
            request = objectStore.openCursor();
            request.onsuccess = function () {
                if (!request.result) {
                    onError2();
                    return;
                }
                data.push(request.result.key);
                request.result.continue();
            };
            break;
        case 'length':
            request = objectStore.count();
            break;
        case 'removeItem':
            request = objectStore.delete(String(options.key));
            break;
        case 'setItem':
            request = objectStore.put(options.value, String(options.key));
            break;
        }
        ['onabort', 'onerror', 'onsuccess'].forEach(function (handler) {
            request[handler] = request[handler] || onError2;
        });
        // debug request
        local._debugStorageRequest = request;
        break;
    case 'node':
        switch (options.action) {
        case 'clear':
            child_process.spawnSync('rm -f ' + storage + '/*', {
                shell: true,
                stdio: ['ignore', 1, 2]
            });
            setTimeout(onError);
            break;
        case 'getItem':
            fs.readFile(
                storage + '/' + encodeURIComponent(String(options.key)),
                'utf8',
                // ignore error
                function (error, data) {
                    onError(error && null, data || '');
                }
            );
            break;
        case 'keys':
            fs.readdir(storage, function (error, data) {
                onError(error, data && data.map(decodeURIComponent));
            });
            break;
        case 'length':
            fs.readdir(storage, function (error, data) {
                onError(error, data && data.length);
            });
            break;
        case 'removeItem':
            fs.unlink(
                storage + '/' + encodeURIComponent(String(options.key)),
                // ignore error
                function () {
                    onError();
                }
            );
            break;
        case 'setItem':
            tmp = os.tmpdir() + '/' + Date.now() + Math.random();
            // save to tmp
            fs.writeFile(tmp, options.value, function (error) {
                // validate no error occurred
                console.assert(!error, error);
                // rename tmp to key
                fs.rename(
                    tmp,
                    storage + '/' + encodeURIComponent(String(options.key)),
                    onError
                );
            }); ...







Or maybe, a large lookup table that takes a while to build, and might
not even be used, so you don't want to do it on load?

```js
var table

function initTable() {
   if (table) return
   table = new Array(10000)
   // do some expensive calculations
}
```

this is a textbook-example for using memoization as alexander mentioned.  here's a real-world memoized file-server solution
middlewareFileServer = function (request, response, nextMiddleware) {
/*
 * this function will run the middleware that will serve files
 */
    if (request.method !== 'GET' || local.modeJs === 'browser') {
        nextMiddleware();
        return;
    }
    request.urlFile = (process.cwd() + request.urlParsed.pathname
        // security - disable parent directory lookup
        .replace((/.*\/\.\.\//g), '/'))
        // replace trailing '/' with '/index.html'
        .replace((/\/$/), '/index.html');
    // serve file from cache
    local.taskCreateCached({
        cacheDict: 'middlewareFileServer',
        key: request.urlFile
    // run background-task to re-cache file
    }, function (onError) {
        local.fs.readFile(request.urlFile, function (error, data) {
            onError(error, data && local.base64FromBuffer(data));
        });
    }, function (error, data) {
        // default to nextMiddleware
        if (error) {
            nextMiddleware();
            return;
        }
        // init response-header content-type
        request.urlParsed.contentType = (/\.[^\.]*$/).exec(request.urlParsed.pathname);
        request.urlParsed.contentType = local.contentTypeDict[
            request.urlParsed.contentType && request.urlParsed.contentType[0]
        ];
        local.serverRespondHeadSet(request, response, null, {
            'Content-Type': request.urlParsed.contentType
        });
        // serve file from cache
        response.end(local.base64ToBuffer(data));
    });
}
taskCreateCached = function (options, onTask, onError) {
/*
 * this function will
 * 1. if cache-hit, then call onError with cacheValue
 * 2. run onTask in background
 * 3. save onTask's result to cache
 * 4. if cache-miss, then call onError with onTask's result
 */
    local.onNext(options, function (error, data) {
        switch (options.modeNext) {
        //  1. if cache-hit, then call onError with cacheValue
        case 1:
            // read cacheValue from memory-cache
            local.cacheDict[options.cacheDict] = local.cacheDict[options.cacheDict] ||
                {};
            options.cacheValue = local.cacheDict[options.cacheDict][options.key];
            if (options.cacheValue) {
                // call onError with cacheValue
                options.modeCacheHit = true;
                onError(null, JSON.parse(options.cacheValue));
                if (!options.modeCacheUpdate) {
                    break;
                }
            }
            // run background-task with lower priority for cache-hit
            setTimeout(options.onNext, options.modeCacheHit && options.cacheTtl);
            break;
        // 2. run onTask in background
        case 2:
            local.taskCreate(options, onTask, options.onNext);
            break;
        default:
            // 3. save onTask's result to cache
            // JSON.stringify data to prevent side-effects on cache
            options.cacheValue = JSON.stringify(data);
            if (!error && options.cacheValue) {
                local.cacheDict[options.cacheDict][options.key] = options.cacheValue;
            }
            // 4. if cache-miss, then call onError with onTask's result
            if (!options.modeCacheHit) {
                onError(error, options.cacheValue && JSON.parse(options.cacheValue));
            }
            (options.onCacheWrite || local.nop)();
            break;
        }
    });
    options.modeNext = 0;
    options.onNext();
}



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

Re: Lazy evaluation

Andrea Giammarchi-2
In reply to this post by Michał Wadas
I thought decorators were nowhere higher than stage 0 (since ever)

On Thu, Aug 31, 2017 at 9:53 PM, Michał Wadas <[hidden email]> wrote:
Why not something like decorators (not sure if decorator  proposal covers this already)? 

class Foo {
@cached
get bar() {
return something(this);

On 31 Aug 2017 10:30 pm, "Andrea Giammarchi" <[hidden email]> wrote:
it's a matter of semantics.

If I see this

```js
var later = anyWrappingName(() => Math.random());

// this is an assumption, not something obvious
later() === later()
```

If instead, I write this:
```js
this.later === this.later;
```

I expect that to never possibly fail like `arr.length === arr.length` or any `obj.prop`, in APIs with common sense, are equal to `obj.prop`.

Invokes via instances and objects? It's never obvious at first look, if that is a method execution, but it's surely a new invoke.

If you've trapped once the result behind the scene, reading that, is just noise for anyone eyes.

So, once again, are we proposing something that results into exactly this?

```js
class Later {
  get thing() {
    return Object.defineProperty(this, 'thing', {value: anyLazy()});
  }
  constructor() {
    // always true, no matter when/where
    this.thing === this.thing;
  }
}
```

If so, I'm happy. If not, this is confusing and solving not much.


Best Regards


On Thu, Aug 31, 2017 at 9:14 PM, Isiah Meadows <[hidden email]> wrote:
Yes. I'll point out that having it as a function, rather than a
property-specific thing, makes it more flexible, since you can define
constants as lazy values (I do that in quite a few places).

If you want to make it transparent, it's not that hard to make a
single-line getter/method that hides the abstraction.

Granted, most of my lazy values are properties, not constants, so I
could consider it an acceptable compromise.
-----

Isiah Meadows
[hidden email]

Looking for web consulting? Or a new website?
Send me an email and we can get started.
www.isiahmeadows.com


On Thu, Aug 31, 2017 at 3:54 PM, Andrea Giammarchi
<[hidden email]> wrote:
> so in JavaScript that results into this._db() each time, resolved lazily
> with the first value returned once ?
>
> I still think my approach is cleaner and more transparent.
>
> `get _thing() { return defineProperty(this, 'thing', value) }`
>
> but if your TS-ish stuff translates into that, works for me
>
>
>
> On Thu, Aug 31, 2017 at 8:49 PM, Isiah Meadows <[hidden email]>
> wrote:
>>
>> It takes a function, and returns a function that (if necessary)
>> initializes the value and then gets it.
>> -----
>>
>> Isiah Meadows
>> [hidden email]
>>
>> Looking for web consulting? Or a new website?
>> Send me an email and we can get started.
>> www.isiahmeadows.com
>>
>>
>> On Thu, Aug 31, 2017 at 3:43 PM, Andrea Giammarchi
>> <[hidden email]> wrote:
>> > Sorry I don't speak TS, I speak ES.
>> >
>> > Can you please tell me in JavaScript what does that do?
>> >
>> > On Thu, Aug 31, 2017 at 8:18 PM, Isiah Meadows <[hidden email]>
>> > wrote:
>> >>
>> >> Note the TS-ish declaration above it. That's the variant I was
>> >> referring to (I presented about 3 different variants initially).
>> >>
>> >> ```ts
>> >> // The declaration I included
>> >> declare function lazy<T>(init: () => T): () => T;
>> >> ```
>> >>
>> >>
>> >> On Thu, Aug 31, 2017 at 3:05 PM, Andrea Giammarchi
>> >> <[hidden email]> wrote:
>> >> > it wouldn't work, would it ? I mean, you still have to pass through
>> >> > the
>> >> > "ugly" _db.get() thingy, right?
>> >> >
>> >> > how do you access and trigger the lazy bit within the class?
>> >> >
>> >> > On Thu, Aug 31, 2017 at 7:56 PM, Isiah Meadows
>> >> > <[hidden email]>
>> >> > wrote:
>> >> >>
>> >> >> What about this (using the stage 3 class fields proposal)?
>> >> >>
>> >> >> ```js
>> >> >> declare function lazy<T>(init: () => T): () => T;
>> >> >>
>> >> >> class WithLazyVals {
>> >> >>     _db = lazy(() => new Promise(...));
>> >> >> }
>> >> >> ```
>> >> >> -----
>> >> >>
>> >> >> Isiah Meadows
>> >> >> [hidden email]
>> >> >>
>> >> >> Looking for web consulting? Or a new website?
>> >> >> Send me an email and we can get started.
>> >> >> www.isiahmeadows.com
>> >> >>
>> >> >>
>> >> >> On Thu, Aug 31, 2017 at 1:34 PM, Andrea Giammarchi
>> >> >> <[hidden email]> wrote:
>> >> >> >> this proposal doesn't compose well with classes
>> >> >> >
>> >> >> > to expand a little, if you were proposing
>> >> >> >
>> >> >> > ```js
>> >> >> > class WithLazyVals {
>> >> >> >   lazy _db() { return new Promise(...); }
>> >> >> > }
>> >> >> > ```
>> >> >> >
>> >> >> > I would've taken first flight to come over and hug you.
>> >> >> >
>> >> >> > Best Regards
>> >> >> >
>> >> >> >
>> >> >> >
>> >> >> >
>> >> >> > On Thu, Aug 31, 2017 at 6:25 PM, Andrea Giammarchi
>> >> >> > <[hidden email]> wrote:
>> >> >> >>
>> >> >> >> > How often do you start out with a class like this ...
>> >> >> >>
>> >> >> >> Never, like I've said. This is the lazy pattern I know since
>> >> >> >> ever.
>> >> >> >>
>> >> >> >> ```js
>> >> >> >> class Foo {
>> >> >> >>   get _db() {
>> >> >> >>     return Object.defineProperty(this, '_db', {
>> >> >> >>       value: new Promise((resolve, reject) => {
>> >> >> >>         // open a database connection
>> >> >> >>         // set up whatever tables you need to
>> >> >> >>         // etc.
>> >> >> >>       })
>> >> >> >>     })._db;
>> >> >> >>   }
>> >> >> >> }
>> >> >> >> ```
>> >> >> >>
>> >> >> >> Whenever you need, you just access `this._db`, no need to create
>> >> >> >> an
>> >> >> >> enumerable variable and a class method.
>> >> >> >>
>> >> >> >> It looks cleaner to me.
>> >> >> >>
>> >> >> >>
>> >> >> >> > Things you don't want to initialize right away because
>> >> >> >> > initialization
>> >> >> >>
>> >> >> >> You don't really have to convince me, I've written lazy
>> >> >> >> properties
>> >> >> >> since
>> >> >> >> getters and setters were introduced [1]
>> >> >> >>
>> >> >> >> All I am saying is that this proposal doesn't compose well with
>> >> >> >> classes,
>> >> >> >> it's just yet another SuperPrimitive for the language.
>> >> >> >>
>> >> >> >> It is also something trivial to implement on user land, yet I
>> >> >> >> haven't
>> >> >> >> seen
>> >> >> >> many writing code like the following:
>> >> >> >>
>> >> >> >> ```js
>> >> >> >> function Lazy(fn) {
>> >> >> >>   let c = false, v;
>> >> >> >>   return {get(){ return c ? v : (c = !c, v = fn()) }};
>> >> >> >> }
>> >> >> >>
>> >> >> >> var o = Lazy(() => Math.random());
>> >> >> >> o.get(); // ...
>> >> >> >> ```
>> >> >> >>
>> >> >> >> Maybe it's me that hasn't seen this widely adopted from some
>> >> >> >> library?
>> >> >> >>
>> >> >> >> Anyway, this is just my opinion, maybe others would be happy with
>> >> >> >> this.
>> >> >> >>
>> >> >> >> Best Regards
>> >> >> >>
>> >> >> >> [1] Class.lazy example
>> >> >> >>
>> >> >> >>
>> >> >> >>
>> >> >> >> https://github.com/WebReflection/prototypal/blob/master/Class.md#classlazycallback
>> >> >> >>
>> >> >> >>
>> >> >> >>
>> >> >> >> On Thu, Aug 31, 2017 at 6:03 PM, Isiah Meadows
>> >> >> >> <[hidden email]>
>> >> >> >> wrote:
>> >> >> >>>
>> >> >> >>> It'd solve a problem similarly to Kotlin's `by lazy { ... }`
>> >> >> >>> delegate,
>> >> >> >>> .NET's `System.Lazy<T>`, Swift's `lazy var`, among many other
>> >> >> >>> languages. It's very useful for lazy initialization [1], such as
>> >> >> >>> lazily setting up a database, requesting a resource, among other
>> >> >> >>> costly things. [2]
>> >> >> >>>
>> >> >> >>> How often do you start out with a class like this, where you
>> >> >> >>> have
>> >> >> >>> an
>> >> >> >>> expensive resource you don't want to open right away?
>> >> >> >>>
>> >> >> >>> ```js
>> >> >> >>> class Foo {
>> >> >> >>>     constructor() {
>> >> >> >>>         this._db = undefined
>> >> >> >>>     }
>> >> >> >>>
>> >> >> >>>     _initDb() {
>> >> >> >>>         if (this._db) return this._db
>> >> >> >>>         return this._db = new Promise((resolve, reject) => {
>> >> >> >>>             // open a database connection
>> >> >> >>>             // set up whatever tables you need to
>> >> >> >>>             // etc.
>> >> >> >>>         })
>> >> >> >>>     }
>> >> >> >>> }
>> >> >> >>> ```
>> >> >> >>>
>> >> >> >>> Or maybe, a large lookup table that takes a while to build, and
>> >> >> >>> might
>> >> >> >>> not even be used, so you don't want to do it on load?
>> >> >> >>>
>> >> >> >>> ```js
>> >> >> >>> var table
>> >> >> >>>
>> >> >> >>> function initTable() {
>> >> >> >>>     if (table) return
>> >> >> >>>     table = new Array(10000)
>> >> >> >>>     // do some expensive calculations
>> >> >> >>> }
>> >> >> >>> ```
>> >> >> >>>
>> >> >> >>> Things you don't want to initialize right away because
>> >> >> >>> initialization
>> >> >> >>> is expensive and/or the value might not even be used. That's the
>> >> >> >>> problem I'm aiming to solve, and it's something I feel would be
>> >> >> >>> useful
>> >> >> >>> in its own right in the language, about equal in importance to
>> >> >> >>> weak
>> >> >> >>> references. (Slightly specialized, but the need is not
>> >> >> >>> non-zero.)
>> >> >> >>>
>> >> >> >>> [1]: https://en.wikipedia.org/wiki/Lazy_initialization
>> >> >> >>> [2]:
>> >> >> >>>
>> >> >> >>>
>> >> >> >>>
>> >> >> >>> https://stackoverflow.com/questions/978759/what-is-lazy-initialization-and-why-is-it-useful
>> >> >> >>> -----
>> >> >> >>>
>> >> >> >>> Isiah Meadows
>> >> >> >>> [hidden email]
>> >> >> >>>
>> >> >> >>> Looking for web consulting? Or a new website?
>> >> >> >>> Send me an email and we can get started.
>> >> >> >>> www.isiahmeadows.com
>> >> >> >>>
>> >> >> >>>
>> >> >> >>> On Thu, Aug 31, 2017 at 12:23 PM, Andrea Giammarchi
>> >> >> >>> <[hidden email]> wrote:
>> >> >> >>> > right ... so ... I'm not sure I understand what this proposal
>> >> >> >>> > would
>> >> >> >>> > solve.
>> >> >> >>> >
>> >> >> >>> > Instead of this:
>> >> >> >>> > ```js
>> >> >> >>> > obj.val || (obj.val = getValue())
>> >> >> >>> > ```
>> >> >> >>> >
>> >> >> >>> > you want to do this
>> >> >> >>> > ```js
>> >> >> >>> > (obj.val || (obj.val = new Lazy(getValue)).get();
>> >> >> >>> > ```
>> >> >> >>> >
>> >> >> >>> > Where is the "win" and why is that?
>> >> >> >>> >
>> >> >> >>> >
>> >> >> >>> >
>> >> >> >>> > On Thu, Aug 31, 2017 at 5:18 PM, Isiah Meadows
>> >> >> >>> > <[hidden email]>
>> >> >> >>> > wrote:
>> >> >> >>> >>
>> >> >> >>> >> With my proposed `Lazy` class, if you were to use an instance
>> >> >> >>> >> as
>> >> >> >>> >> a
>> >> >> >>> >> descriptor, the `this` value it'd receive would not be a
>> >> >> >>> >> `Lazy`
>> >> >> >>> >> instance like it'd expect.
>> >> >> >>> >>
>> >> >> >>> >> Consider it the difference between `a.self` and `b.get()` in
>> >> >> >>> >> your
>> >> >> >>> >> example. `b.get()` is what I'd be expecting.
>> >> >> >>> >> -----
>> >> >> >>> >>
>> >> >> >>> >> Isiah Meadows
>> >> >> >>> >> [hidden email]
>> >> >> >>> >>
>> >> >> >>> >> Looking for web consulting? Or a new website?
>> >> >> >>> >> Send me an email and we can get started.
>> >> >> >>> >> www.isiahmeadows.com
>> >> >> >>> >>
>> >> >> >>> >>
>> >> >> >>> >> On Thu, Aug 31, 2017 at 12:12 PM, Andrea Giammarchi
>> >> >> >>> >> <[hidden email]> wrote:
>> >> >> >>> >> >> using it in a descriptor would get it passed the wrong
>> >> >> >>> >> >> `this`
>> >> >> >>> >> >
>> >> >> >>> >> > sorry, what?
>> >> >> >>> >> >
>> >> >> >>> >> > ```js
>> >> >> >>> >> > var a = {};
>> >> >> >>> >> > var b = {get() { return this; }};
>> >> >> >>> >> > Object.defineProperty(a, 'self', b);
>> >> >> >>> >> >
>> >> >> >>> >> > a.self === a; // true
>> >> >> >>> >> > ```
>> >> >> >>> >> >
>> >> >> >>> >> >
>> >> >> >>> >> > On Thu, Aug 31, 2017 at 5:09 PM, Isiah Meadows
>> >> >> >>> >> > <[hidden email]>
>> >> >> >>> >> > wrote:
>> >> >> >>> >> >>
>> >> >> >>> >> >> No. `Lazy` is intended to be an object to be used
>> >> >> >>> >> >> directly,
>> >> >> >>> >> >> not
>> >> >> >>> >> >> a
>> >> >> >>> >> >> descriptor of any kind.
>> >> >> >>> >> >>
>> >> >> >>> >> >> (My `lazy.get()` is an unbound method, so using it in a
>> >> >> >>> >> >> descriptor
>> >> >> >>> >> >> would get it passed the wrong `this`.)
>> >> >> >>> >> >> -----
>> >> >> >>> >> >>
>> >> >> >>> >> >> Isiah Meadows
>> >> >> >>> >> >> [hidden email]
>> >> >> >>> >> >>
>> >> >> >>> >> >> Looking for web consulting? Or a new website?
>> >> >> >>> >> >> Send me an email and we can get started.
>> >> >> >>> >> >> www.isiahmeadows.com
>> >> >> >>> >> >>
>> >> >> >>> >> >>
>> >> >> >>> >> >> On Thu, Aug 31, 2017 at 9:39 AM, Andrea Giammarchi
>> >> >> >>> >> >> <[hidden email]> wrote:
>> >> >> >>> >> >> > the following is how I usually consider lazy values
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > ```js
>> >> >> >>> >> >> > class Any {
>> >> >> >>> >> >> >   _lazy(name) {
>> >> >> >>> >> >> >     switch (name) {
>> >> >> >>> >> >> >       case 'uid': return Math.random();
>> >> >> >>> >> >> >       // others ... eventually
>> >> >> >>> >> >> >     }
>> >> >> >>> >> >> >   }
>> >> >> >>> >> >> >   get uid() {
>> >> >> >>> >> >> >     var value = this._lazy('uid');
>> >> >> >>> >> >> >     // from now on, direct access
>> >> >> >>> >> >> >     Object.defineProperty(this, 'uid', {value});
>> >> >> >>> >> >> >     return value;
>> >> >> >>> >> >> >   }
>> >> >> >>> >> >> > }
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > const a = new Any;
>> >> >> >>> >> >> > a.uid === a.uid; // true
>> >> >> >>> >> >> > ```
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > If I understand correctly your proposal is to use Lazy
>> >> >> >>> >> >> > as
>> >> >> >>> >> >> > generic
>> >> >> >>> >> >> > descriptor, is that correct ?
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > ```js
>> >> >> >>> >> >> > Object.defineProperty({}, 'something', new Lazy(function
>> >> >> >>> >> >> > (val)
>> >> >> >>> >> >> > {
>> >> >> >>> >> >> >   return this.shakaLaka ? val : 'no shakaLaka';
>> >> >> >>> >> >> > }));
>> >> >> >>> >> >> > ```
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > ???
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > If that's the case I see already people confused by
>> >> >> >>> >> >> > arrow
>> >> >> >>> >> >> > function
>> >> >> >>> >> >> > in case they need to access the context,
>> >> >> >>> >> >> > plus no property access optimization once resolved.
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > It's also not clear if such property can be set again
>> >> >> >>> >> >> > later
>> >> >> >>> >> >> > on
>> >> >> >>> >> >> > (right
>> >> >> >>> >> >> > now it
>> >> >> >>> >> >> > cannot)
>> >> >> >>> >> >> > 'cause lazy definition doesn't always necessarily mean
>> >> >> >>> >> >> > inability
>> >> >> >>> >> >> > to
>> >> >> >>> >> >> > reassign.
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > What am I missing/misunderstanding?
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > Regards
>> >> >> >>> >> >> >
>> >> >> >>> >> >> >
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > On Thu, Aug 31, 2017 at 2:21 PM, Isiah Meadows
>> >> >> >>> >> >> > <[hidden email]>
>> >> >> >>> >> >> > wrote:
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> It'd be really nice if lazy values made it into the
>> >> >> >>> >> >> >> spec
>> >> >> >>> >> >> >> somehow.
>> >> >> >>> >> >> >> I've
>> >> >> >>> >> >> >> already found myself using things like this [1] quite a
>> >> >> >>> >> >> >> bit,
>> >> >> >>> >> >> >> and
>> >> >> >>> >> >> >> I've
>> >> >> >>> >> >> >> also found myself frequently initializing properties
>> >> >> >>> >> >> >> not
>> >> >> >>> >> >> >> on
>> >> >> >>> >> >> >> first
>> >> >> >>> >> >> >> access.
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> [1]:
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> https://gist.github.com/isiahmeadows/4c0723bdfa555a1c2cb01341b323c3d4
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> As for what would be a nice API, maybe something like
>> >> >> >>> >> >> >> one
>> >> >> >>> >> >> >> of
>> >> >> >>> >> >> >> these?
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> ```js
>> >> >> >>> >> >> >> class Lazy<T> {
>> >> >> >>> >> >> >>     constructor(init: () => T);
>> >> >> >>> >> >> >>     get(): T; // or error thrown
>> >> >> >>> >> >> >> }
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> function lazy<T>(init: () => T): () => T; // or error
>> >> >> >>> >> >> >> thrown
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> function lazy<T>(init: () => T): {
>> >> >> >>> >> >> >>     get(): T; // or error thrown
>> >> >> >>> >> >> >> }
>> >> >> >>> >> >> >> ```
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> Alternatively, syntax might work, with `do` expression
>> >> >> >>> >> >> >> semantics:
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> ```js
>> >> >> >>> >> >> >> const x = lazy do { ... }
>> >> >> >>> >> >> >> // expose via `x.get()` or just `x()`
>> >> >> >>> >> >> >> ```
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> -----
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> Isiah Meadows
>> >> >> >>> >> >> >> [hidden email]
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> Looking for web consulting? Or a new website?
>> >> >> >>> >> >> >> Send me an email and we can get started.
>> >> >> >>> >> >> >> www.isiahmeadows.com
>> >> >> >>> >> >> >> _______________________________________________
>> >> >> >>> >> >> >> es-discuss mailing list
>> >> >> >>> >> >> >> [hidden email]
>> >> >> >>> >> >> >> https://mail.mozilla.org/listinfo/es-discuss
>> >> >> >>> >> >> >
>> >> >> >>> >> >> >
>> >> >> >>> >> >
>> >> >> >>> >> >
>> >> >> >>> >
>> >> >> >>> >
>> >> >> >>
>> >> >> >>
>> >> >> >
>> >> >
>> >> >
>> >>
>> >> -----
>> >>
>> >> Isiah Meadows
>> >> [hidden email]
>> >>
>> >> Looking for web consulting? Or a new website?
>> >> Send me an email and we can get started.
>> >> www.isiahmeadows.com
>> >
>> >
>
>


_______________________________________________
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: Lazy evaluation

Michał Wadas
Stage 2, but they move really slow. 

On 1 Sep 2017 9:15 am, "Andrea Giammarchi" <[hidden email]> wrote:
I thought decorators were nowhere higher than stage 0 (since ever)

On Thu, Aug 31, 2017 at 9:53 PM, Michał Wadas <[hidden email]> wrote:
Why not something like decorators (not sure if decorator  proposal covers this already)? 

class Foo {
@cached
get bar() {
return something(this);

On 31 Aug 2017 10:30 pm, "Andrea Giammarchi" <[hidden email]> wrote:
it's a matter of semantics.

If I see this

```js
var later = anyWrappingName(() => Math.random());

// this is an assumption, not something obvious
later() === later()
```

If instead, I write this:
```js
this.later === this.later;
```

I expect that to never possibly fail like `arr.length === arr.length` or any `obj.prop`, in APIs with common sense, are equal to `obj.prop`.

Invokes via instances and objects? It's never obvious at first look, if that is a method execution, but it's surely a new invoke.

If you've trapped once the result behind the scene, reading that, is just noise for anyone eyes.

So, once again, are we proposing something that results into exactly this?

```js
class Later {
  get thing() {
    return Object.defineProperty(this, 'thing', {value: anyLazy()});
  }
  constructor() {
    // always true, no matter when/where
    this.thing === this.thing;
  }
}
```

If so, I'm happy. If not, this is confusing and solving not much.


Best Regards


On Thu, Aug 31, 2017 at 9:14 PM, Isiah Meadows <[hidden email]> wrote:
Yes. I'll point out that having it as a function, rather than a
property-specific thing, makes it more flexible, since you can define
constants as lazy values (I do that in quite a few places).

If you want to make it transparent, it's not that hard to make a
single-line getter/method that hides the abstraction.

Granted, most of my lazy values are properties, not constants, so I
could consider it an acceptable compromise.
-----

Isiah Meadows
[hidden email]

Looking for web consulting? Or a new website?
Send me an email and we can get started.
www.isiahmeadows.com


On Thu, Aug 31, 2017 at 3:54 PM, Andrea Giammarchi
<[hidden email]> wrote:
> so in JavaScript that results into this._db() each time, resolved lazily
> with the first value returned once ?
>
> I still think my approach is cleaner and more transparent.
>
> `get _thing() { return defineProperty(this, 'thing', value) }`
>
> but if your TS-ish stuff translates into that, works for me
>
>
>
> On Thu, Aug 31, 2017 at 8:49 PM, Isiah Meadows <[hidden email]>
> wrote:
>>
>> It takes a function, and returns a function that (if necessary)
>> initializes the value and then gets it.
>> -----
>>
>> Isiah Meadows
>> [hidden email]
>>
>> Looking for web consulting? Or a new website?
>> Send me an email and we can get started.
>> www.isiahmeadows.com
>>
>>
>> On Thu, Aug 31, 2017 at 3:43 PM, Andrea Giammarchi
>> <[hidden email]> wrote:
>> > Sorry I don't speak TS, I speak ES.
>> >
>> > Can you please tell me in JavaScript what does that do?
>> >
>> > On Thu, Aug 31, 2017 at 8:18 PM, Isiah Meadows <[hidden email]>
>> > wrote:
>> >>
>> >> Note the TS-ish declaration above it. That's the variant I was
>> >> referring to (I presented about 3 different variants initially).
>> >>
>> >> ```ts
>> >> // The declaration I included
>> >> declare function lazy<T>(init: () => T): () => T;
>> >> ```
>> >>
>> >>
>> >> On Thu, Aug 31, 2017 at 3:05 PM, Andrea Giammarchi
>> >> <[hidden email]> wrote:
>> >> > it wouldn't work, would it ? I mean, you still have to pass through
>> >> > the
>> >> > "ugly" _db.get() thingy, right?
>> >> >
>> >> > how do you access and trigger the lazy bit within the class?
>> >> >
>> >> > On Thu, Aug 31, 2017 at 7:56 PM, Isiah Meadows
>> >> > <[hidden email]>
>> >> > wrote:
>> >> >>
>> >> >> What about this (using the stage 3 class fields proposal)?
>> >> >>
>> >> >> ```js
>> >> >> declare function lazy<T>(init: () => T): () => T;
>> >> >>
>> >> >> class WithLazyVals {
>> >> >>     _db = lazy(() => new Promise(...));
>> >> >> }
>> >> >> ```
>> >> >> -----
>> >> >>
>> >> >> Isiah Meadows
>> >> >> [hidden email]
>> >> >>
>> >> >> Looking for web consulting? Or a new website?
>> >> >> Send me an email and we can get started.
>> >> >> www.isiahmeadows.com
>> >> >>
>> >> >>
>> >> >> On Thu, Aug 31, 2017 at 1:34 PM, Andrea Giammarchi
>> >> >> <[hidden email]> wrote:
>> >> >> >> this proposal doesn't compose well with classes
>> >> >> >
>> >> >> > to expand a little, if you were proposing
>> >> >> >
>> >> >> > ```js
>> >> >> > class WithLazyVals {
>> >> >> >   lazy _db() { return new Promise(...); }
>> >> >> > }
>> >> >> > ```
>> >> >> >
>> >> >> > I would've taken first flight to come over and hug you.
>> >> >> >
>> >> >> > Best Regards
>> >> >> >
>> >> >> >
>> >> >> >
>> >> >> >
>> >> >> > On Thu, Aug 31, 2017 at 6:25 PM, Andrea Giammarchi
>> >> >> > <[hidden email]> wrote:
>> >> >> >>
>> >> >> >> > How often do you start out with a class like this ...
>> >> >> >>
>> >> >> >> Never, like I've said. This is the lazy pattern I know since
>> >> >> >> ever.
>> >> >> >>
>> >> >> >> ```js
>> >> >> >> class Foo {
>> >> >> >>   get _db() {
>> >> >> >>     return Object.defineProperty(this, '_db', {
>> >> >> >>       value: new Promise((resolve, reject) => {
>> >> >> >>         // open a database connection
>> >> >> >>         // set up whatever tables you need to
>> >> >> >>         // etc.
>> >> >> >>       })
>> >> >> >>     })._db;
>> >> >> >>   }
>> >> >> >> }
>> >> >> >> ```
>> >> >> >>
>> >> >> >> Whenever you need, you just access `this._db`, no need to create
>> >> >> >> an
>> >> >> >> enumerable variable and a class method.
>> >> >> >>
>> >> >> >> It looks cleaner to me.
>> >> >> >>
>> >> >> >>
>> >> >> >> > Things you don't want to initialize right away because
>> >> >> >> > initialization
>> >> >> >>
>> >> >> >> You don't really have to convince me, I've written lazy
>> >> >> >> properties
>> >> >> >> since
>> >> >> >> getters and setters were introduced [1]
>> >> >> >>
>> >> >> >> All I am saying is that this proposal doesn't compose well with
>> >> >> >> classes,
>> >> >> >> it's just yet another SuperPrimitive for the language.
>> >> >> >>
>> >> >> >> It is also something trivial to implement on user land, yet I
>> >> >> >> haven't
>> >> >> >> seen
>> >> >> >> many writing code like the following:
>> >> >> >>
>> >> >> >> ```js
>> >> >> >> function Lazy(fn) {
>> >> >> >>   let c = false, v;
>> >> >> >>   return {get(){ return c ? v : (c = !c, v = fn()) }};
>> >> >> >> }
>> >> >> >>
>> >> >> >> var o = Lazy(() => Math.random());
>> >> >> >> o.get(); // ...
>> >> >> >> ```
>> >> >> >>
>> >> >> >> Maybe it's me that hasn't seen this widely adopted from some
>> >> >> >> library?
>> >> >> >>
>> >> >> >> Anyway, this is just my opinion, maybe others would be happy with
>> >> >> >> this.
>> >> >> >>
>> >> >> >> Best Regards
>> >> >> >>
>> >> >> >> [1] Class.lazy example
>> >> >> >>
>> >> >> >>
>> >> >> >>
>> >> >> >> https://github.com/WebReflection/prototypal/blob/master/Class.md#classlazycallback
>> >> >> >>
>> >> >> >>
>> >> >> >>
>> >> >> >> On Thu, Aug 31, 2017 at 6:03 PM, Isiah Meadows
>> >> >> >> <[hidden email]>
>> >> >> >> wrote:
>> >> >> >>>
>> >> >> >>> It'd solve a problem similarly to Kotlin's `by lazy { ... }`
>> >> >> >>> delegate,
>> >> >> >>> .NET's `System.Lazy<T>`, Swift's `lazy var`, among many other
>> >> >> >>> languages. It's very useful for lazy initialization [1], such as
>> >> >> >>> lazily setting up a database, requesting a resource, among other
>> >> >> >>> costly things. [2]
>> >> >> >>>
>> >> >> >>> How often do you start out with a class like this, where you
>> >> >> >>> have
>> >> >> >>> an
>> >> >> >>> expensive resource you don't want to open right away?
>> >> >> >>>
>> >> >> >>> ```js
>> >> >> >>> class Foo {
>> >> >> >>>     constructor() {
>> >> >> >>>         this._db = undefined
>> >> >> >>>     }
>> >> >> >>>
>> >> >> >>>     _initDb() {
>> >> >> >>>         if (this._db) return this._db
>> >> >> >>>         return this._db = new Promise((resolve, reject) => {
>> >> >> >>>             // open a database connection
>> >> >> >>>             // set up whatever tables you need to
>> >> >> >>>             // etc.
>> >> >> >>>         })
>> >> >> >>>     }
>> >> >> >>> }
>> >> >> >>> ```
>> >> >> >>>
>> >> >> >>> Or maybe, a large lookup table that takes a while to build, and
>> >> >> >>> might
>> >> >> >>> not even be used, so you don't want to do it on load?
>> >> >> >>>
>> >> >> >>> ```js
>> >> >> >>> var table
>> >> >> >>>
>> >> >> >>> function initTable() {
>> >> >> >>>     if (table) return
>> >> >> >>>     table = new Array(10000)
>> >> >> >>>     // do some expensive calculations
>> >> >> >>> }
>> >> >> >>> ```
>> >> >> >>>
>> >> >> >>> Things you don't want to initialize right away because
>> >> >> >>> initialization
>> >> >> >>> is expensive and/or the value might not even be used. That's the
>> >> >> >>> problem I'm aiming to solve, and it's something I feel would be
>> >> >> >>> useful
>> >> >> >>> in its own right in the language, about equal in importance to
>> >> >> >>> weak
>> >> >> >>> references. (Slightly specialized, but the need is not
>> >> >> >>> non-zero.)
>> >> >> >>>
>> >> >> >>> [1]: https://en.wikipedia.org/wiki/Lazy_initialization
>> >> >> >>> [2]:
>> >> >> >>>
>> >> >> >>>
>> >> >> >>>
>> >> >> >>> https://stackoverflow.com/questions/978759/what-is-lazy-initialization-and-why-is-it-useful
>> >> >> >>> -----
>> >> >> >>>
>> >> >> >>> Isiah Meadows
>> >> >> >>> [hidden email]
>> >> >> >>>
>> >> >> >>> Looking for web consulting? Or a new website?
>> >> >> >>> Send me an email and we can get started.
>> >> >> >>> www.isiahmeadows.com
>> >> >> >>>
>> >> >> >>>
>> >> >> >>> On Thu, Aug 31, 2017 at 12:23 PM, Andrea Giammarchi
>> >> >> >>> <[hidden email]> wrote:
>> >> >> >>> > right ... so ... I'm not sure I understand what this proposal
>> >> >> >>> > would
>> >> >> >>> > solve.
>> >> >> >>> >
>> >> >> >>> > Instead of this:
>> >> >> >>> > ```js
>> >> >> >>> > obj.val || (obj.val = getValue())
>> >> >> >>> > ```
>> >> >> >>> >
>> >> >> >>> > you want to do this
>> >> >> >>> > ```js
>> >> >> >>> > (obj.val || (obj.val = new Lazy(getValue)).get();
>> >> >> >>> > ```
>> >> >> >>> >
>> >> >> >>> > Where is the "win" and why is that?
>> >> >> >>> >
>> >> >> >>> >
>> >> >> >>> >
>> >> >> >>> > On Thu, Aug 31, 2017 at 5:18 PM, Isiah Meadows
>> >> >> >>> > <[hidden email]>
>> >> >> >>> > wrote:
>> >> >> >>> >>
>> >> >> >>> >> With my proposed `Lazy` class, if you were to use an instance
>> >> >> >>> >> as
>> >> >> >>> >> a
>> >> >> >>> >> descriptor, the `this` value it'd receive would not be a
>> >> >> >>> >> `Lazy`
>> >> >> >>> >> instance like it'd expect.
>> >> >> >>> >>
>> >> >> >>> >> Consider it the difference between `a.self` and `b.get()` in
>> >> >> >>> >> your
>> >> >> >>> >> example. `b.get()` is what I'd be expecting.
>> >> >> >>> >> -----
>> >> >> >>> >>
>> >> >> >>> >> Isiah Meadows
>> >> >> >>> >> [hidden email]
>> >> >> >>> >>
>> >> >> >>> >> Looking for web consulting? Or a new website?
>> >> >> >>> >> Send me an email and we can get started.
>> >> >> >>> >> www.isiahmeadows.com
>> >> >> >>> >>
>> >> >> >>> >>
>> >> >> >>> >> On Thu, Aug 31, 2017 at 12:12 PM, Andrea Giammarchi
>> >> >> >>> >> <[hidden email]> wrote:
>> >> >> >>> >> >> using it in a descriptor would get it passed the wrong
>> >> >> >>> >> >> `this`
>> >> >> >>> >> >
>> >> >> >>> >> > sorry, what?
>> >> >> >>> >> >
>> >> >> >>> >> > ```js
>> >> >> >>> >> > var a = {};
>> >> >> >>> >> > var b = {get() { return this; }};
>> >> >> >>> >> > Object.defineProperty(a, 'self', b);
>> >> >> >>> >> >
>> >> >> >>> >> > a.self === a; // true
>> >> >> >>> >> > ```
>> >> >> >>> >> >
>> >> >> >>> >> >
>> >> >> >>> >> > On Thu, Aug 31, 2017 at 5:09 PM, Isiah Meadows
>> >> >> >>> >> > <[hidden email]>
>> >> >> >>> >> > wrote:
>> >> >> >>> >> >>
>> >> >> >>> >> >> No. `Lazy` is intended to be an object to be used
>> >> >> >>> >> >> directly,
>> >> >> >>> >> >> not
>> >> >> >>> >> >> a
>> >> >> >>> >> >> descriptor of any kind.
>> >> >> >>> >> >>
>> >> >> >>> >> >> (My `lazy.get()` is an unbound method, so using it in a
>> >> >> >>> >> >> descriptor
>> >> >> >>> >> >> would get it passed the wrong `this`.)
>> >> >> >>> >> >> -----
>> >> >> >>> >> >>
>> >> >> >>> >> >> Isiah Meadows
>> >> >> >>> >> >> [hidden email]
>> >> >> >>> >> >>
>> >> >> >>> >> >> Looking for web consulting? Or a new website?
>> >> >> >>> >> >> Send me an email and we can get started.
>> >> >> >>> >> >> www.isiahmeadows.com
>> >> >> >>> >> >>
>> >> >> >>> >> >>
>> >> >> >>> >> >> On Thu, Aug 31, 2017 at 9:39 AM, Andrea Giammarchi
>> >> >> >>> >> >> <[hidden email]> wrote:
>> >> >> >>> >> >> > the following is how I usually consider lazy values
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > ```js
>> >> >> >>> >> >> > class Any {
>> >> >> >>> >> >> >   _lazy(name) {
>> >> >> >>> >> >> >     switch (name) {
>> >> >> >>> >> >> >       case 'uid': return Math.random();
>> >> >> >>> >> >> >       // others ... eventually
>> >> >> >>> >> >> >     }
>> >> >> >>> >> >> >   }
>> >> >> >>> >> >> >   get uid() {
>> >> >> >>> >> >> >     var value = this._lazy('uid');
>> >> >> >>> >> >> >     // from now on, direct access
>> >> >> >>> >> >> >     Object.defineProperty(this, 'uid', {value});
>> >> >> >>> >> >> >     return value;
>> >> >> >>> >> >> >   }
>> >> >> >>> >> >> > }
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > const a = new Any;
>> >> >> >>> >> >> > a.uid === a.uid; // true
>> >> >> >>> >> >> > ```
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > If I understand correctly your proposal is to use Lazy
>> >> >> >>> >> >> > as
>> >> >> >>> >> >> > generic
>> >> >> >>> >> >> > descriptor, is that correct ?
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > ```js
>> >> >> >>> >> >> > Object.defineProperty({}, 'something', new Lazy(function
>> >> >> >>> >> >> > (val)
>> >> >> >>> >> >> > {
>> >> >> >>> >> >> >   return this.shakaLaka ? val : 'no shakaLaka';
>> >> >> >>> >> >> > }));
>> >> >> >>> >> >> > ```
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > ???
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > If that's the case I see already people confused by
>> >> >> >>> >> >> > arrow
>> >> >> >>> >> >> > function
>> >> >> >>> >> >> > in case they need to access the context,
>> >> >> >>> >> >> > plus no property access optimization once resolved.
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > It's also not clear if such property can be set again
>> >> >> >>> >> >> > later
>> >> >> >>> >> >> > on
>> >> >> >>> >> >> > (right
>> >> >> >>> >> >> > now it
>> >> >> >>> >> >> > cannot)
>> >> >> >>> >> >> > 'cause lazy definition doesn't always necessarily mean
>> >> >> >>> >> >> > inability
>> >> >> >>> >> >> > to
>> >> >> >>> >> >> > reassign.
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > What am I missing/misunderstanding?
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > Regards
>> >> >> >>> >> >> >
>> >> >> >>> >> >> >
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > On Thu, Aug 31, 2017 at 2:21 PM, Isiah Meadows
>> >> >> >>> >> >> > <[hidden email]>
>> >> >> >>> >> >> > wrote:
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> It'd be really nice if lazy values made it into the
>> >> >> >>> >> >> >> spec
>> >> >> >>> >> >> >> somehow.
>> >> >> >>> >> >> >> I've
>> >> >> >>> >> >> >> already found myself using things like this [1] quite a
>> >> >> >>> >> >> >> bit,
>> >> >> >>> >> >> >> and
>> >> >> >>> >> >> >> I've
>> >> >> >>> >> >> >> also found myself frequently initializing properties
>> >> >> >>> >> >> >> not
>> >> >> >>> >> >> >> on
>> >> >> >>> >> >> >> first
>> >> >> >>> >> >> >> access.
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> [1]:
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> https://gist.github.com/isiahmeadows/4c0723bdfa555a1c2cb01341b323c3d4
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> As for what would be a nice API, maybe something like
>> >> >> >>> >> >> >> one
>> >> >> >>> >> >> >> of
>> >> >> >>> >> >> >> these?
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> ```js
>> >> >> >>> >> >> >> class Lazy<T> {
>> >> >> >>> >> >> >>     constructor(init: () => T);
>> >> >> >>> >> >> >>     get(): T; // or error thrown
>> >> >> >>> >> >> >> }
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> function lazy<T>(init: () => T): () => T; // or error
>> >> >> >>> >> >> >> thrown
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> function lazy<T>(init: () => T): {
>> >> >> >>> >> >> >>     get(): T; // or error thrown
>> >> >> >>> >> >> >> }
>> >> >> >>> >> >> >> ```
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> Alternatively, syntax might work, with `do` expression
>> >> >> >>> >> >> >> semantics:
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> ```js
>> >> >> >>> >> >> >> const x = lazy do { ... }
>> >> >> >>> >> >> >> // expose via `x.get()` or just `x()`
>> >> >> >>> >> >> >> ```
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> -----
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> Isiah Meadows
>> >> >> >>> >> >> >> [hidden email]
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> Looking for web consulting? Or a new website?
>> >> >> >>> >> >> >> Send me an email and we can get started.
>> >> >> >>> >> >> >> www.isiahmeadows.com
>> >> >> >>> >> >> >> _______________________________________________
>> >> >> >>> >> >> >> es-discuss mailing list
>> >> >> >>> >> >> >> [hidden email]
>> >> >> >>> >> >> >> https://mail.mozilla.org/listinfo/es-discuss
>> >> >> >>> >> >> >
>> >> >> >>> >> >> >
>> >> >> >>> >> >
>> >> >> >>> >> >
>> >> >> >>> >
>> >> >> >>> >
>> >> >> >>
>> >> >> >>
>> >> >> >
>> >> >
>> >> >
>> >>
>> >> -----
>> >>
>> >> Isiah Meadows
>> >> [hidden email]
>> >>
>> >> Looking for web consulting? Or a new website?
>> >> Send me an email and we can get started.
>> >> www.isiahmeadows.com
>> >
>> >
>
>


_______________________________________________
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: Lazy evaluation

kdex
Just so that there is no confusion: There's also function expression
decorators and method parameter decorators, both of which stage 0.

On Friday, September 1, 2017 9:16:55 AM CEST Michał Wadas wrote:

> Stage 2, but they move really slow.
>
> On 1 Sep 2017 9:15 am, "Andrea Giammarchi" <[hidden email]>
>
> wrote:
> > I thought decorators were nowhere higher than stage 0 (since ever)
> >
> > On Thu, Aug 31, 2017 at 9:53 PM, Michał Wadas <[hidden email]>
> >
> > wrote:
> >> Why not something like decorators (not sure if decorator  proposal covers
> >> this already)?
> >>
> >> class Foo {
> >> @cached
> >> get bar() {
> >> return something(this);
> >> }
> >> }
> >>
> >> On 31 Aug 2017 10:30 pm, "Andrea Giammarchi"
> >> <[hidden email]>
> >> wrote:
> >>
> >> it's a matter of semantics.
> >>
> >> If I see this
> >>
> >> ```js
> >> var later = anyWrappingName(() => Math.random());
> >>
> >> // this is an assumption, not something obvious
> >> later() === later()
> >> ```
> >>
> >> If instead, I write this:
> >> ```js
> >> this.later === this.later;
> >> ```
> >>
> >> I expect that to never possibly fail like `arr.length === arr.length` or
> >> any `obj.prop`, in APIs with common sense, are equal to `obj.prop`.
> >>
> >> Invokes via instances and objects? It's never obvious at first look, if
> >> that is a method execution, but it's surely a new invoke.
> >>
> >> If you've trapped once the result behind the scene, reading that, is just
> >> noise for anyone eyes.
> >>
> >> So, once again, are we proposing something that results into exactly
> >> this?
> >>
> >> ```js
> >> class Later {
> >>
> >>   get thing() {
> >>  
> >>     return Object.defineProperty(this, 'thing', {value: anyLazy()});
> >>  
> >>   }
> >>   constructor() {
> >>  
> >>     // always true, no matter when/where
> >>     this.thing === this.thing;
> >>  
> >>   }
> >>
> >> }
> >> ```
> >>
> >> If so, I'm happy. If not, this is confusing and solving not much.
> >>
> >>
> >> Best Regards
> >>
> >>
> >> On Thu, Aug 31, 2017 at 9:14 PM, Isiah Meadows <[hidden email]>
> >>
> >> wrote:
> >>> Yes. I'll point out that having it as a function, rather than a
> >>> property-specific thing, makes it more flexible, since you can define
> >>> constants as lazy values (I do that in quite a few places).
> >>>
> >>> If you want to make it transparent, it's not that hard to make a
> >>> single-line getter/method that hides the abstraction.
> >>>
> >>> Granted, most of my lazy values are properties, not constants, so I
> >>> could consider it an acceptable compromise.
> >>> -----
> >>>
> >>> Isiah Meadows
> >>> [hidden email]
> >>>
> >>> Looking for web consulting? Or a new website?
> >>> Send me an email and we can get started.
> >>> www.isiahmeadows.com
> >>>
> >>>
> >>> On Thu, Aug 31, 2017 at 3:54 PM, Andrea Giammarchi
> >>>
> >>> <[hidden email]> wrote:
> >>> > so in JavaScript that results into this._db() each time, resolved
> >>>
> >>> lazily
> >>>
> >>> > with the first value returned once ?
> >>> >
> >>> > I still think my approach is cleaner and more transparent.
> >>> >
> >>> > `get _thing() { return defineProperty(this, 'thing', value) }`
> >>> >
> >>> > but if your TS-ish stuff translates into that, works for me
> >>> >
> >>> >
> >>> >
> >>> > On Thu, Aug 31, 2017 at 8:49 PM, Isiah Meadows <[hidden email]
> >>> >
> >>> > wrote:
> >>> >> It takes a function, and returns a function that (if necessary)
> >>> >> initializes the value and then gets it.
> >>> >> -----
> >>> >>
> >>> >> Isiah Meadows
> >>> >> [hidden email]
> >>> >>
> >>> >> Looking for web consulting? Or a new website?
> >>> >> Send me an email and we can get started.
> >>> >> www.isiahmeadows.com
> >>> >>
> >>> >>
> >>> >> On Thu, Aug 31, 2017 at 3:43 PM, Andrea Giammarchi
> >>> >>
> >>> >> <[hidden email]> wrote:
> >>> >> > Sorry I don't speak TS, I speak ES.
> >>> >> >
> >>> >> > Can you please tell me in JavaScript what does that do?
> >>> >> >
> >>> >> > On Thu, Aug 31, 2017 at 8:18 PM, Isiah Meadows <
> >>>
> >>> [hidden email]>
> >>>
> >>> >> > wrote:
> >>> >> >> Note the TS-ish declaration above it. That's the variant I was
> >>> >> >> referring to (I presented about 3 different variants initially).
> >>> >> >>
> >>> >> >> ```ts
> >>> >> >> // The declaration I included
> >>> >> >> declare function lazy<T>(init: () => T): () => T;
> >>> >> >> ```
> >>> >> >>
> >>> >> >>
> >>> >> >> On Thu, Aug 31, 2017 at 3:05 PM, Andrea Giammarchi
> >>> >> >>
> >>> >> >> <[hidden email]> wrote:
> >>> >> >> > it wouldn't work, would it ? I mean, you still have to pass
> >>>
> >>> through
> >>>
> >>> >> >> > the
> >>> >> >> > "ugly" _db.get() thingy, right?
> >>> >> >> >
> >>> >> >> > how do you access and trigger the lazy bit within the class?
> >>> >> >> >
> >>> >> >> > On Thu, Aug 31, 2017 at 7:56 PM, Isiah Meadows
> >>> >> >> > <[hidden email]>
> >>> >> >> >
> >>> >> >> > wrote:
> >>> >> >> >> What about this (using the stage 3 class fields proposal)?
> >>> >> >> >>
> >>> >> >> >> ```js
> >>> >> >> >> declare function lazy<T>(init: () => T): () => T;
> >>> >> >> >>
> >>> >> >> >> class WithLazyVals {
> >>> >> >> >>
> >>> >> >> >>     _db = lazy(() => new Promise(...));
> >>> >> >> >>
> >>> >> >> >> }
> >>> >> >> >> ```
> >>> >> >> >> -----
> >>> >> >> >>
> >>> >> >> >> Isiah Meadows
> >>> >> >> >> [hidden email]
> >>> >> >> >>
> >>> >> >> >> Looking for web consulting? Or a new website?
> >>> >> >> >> Send me an email and we can get started.
> >>> >> >> >> www.isiahmeadows.com
> >>> >> >> >>
> >>> >> >> >>
> >>> >> >> >> On Thu, Aug 31, 2017 at 1:34 PM, Andrea Giammarchi
> >>> >> >> >>
> >>> >> >> >> <[hidden email]> wrote:
> >>> >> >> >> >> this proposal doesn't compose well with classes
> >>> >> >> >> >
> >>> >> >> >> > to expand a little, if you were proposing
> >>> >> >> >> >
> >>> >> >> >> > ```js
> >>> >> >> >> > class WithLazyVals {
> >>> >> >> >> >
> >>> >> >> >> >   lazy _db() { return new Promise(...); }
> >>> >> >> >> >
> >>> >> >> >> > }
> >>> >> >> >> > ```
> >>> >> >> >> >
> >>> >> >> >> > I would've taken first flight to come over and hug you.
> >>> >> >> >> >
> >>> >> >> >> > Best Regards
> >>> >> >> >> >
> >>> >> >> >> >
> >>> >> >> >> >
> >>> >> >> >> >
> >>> >> >> >> > On Thu, Aug 31, 2017 at 6:25 PM, Andrea Giammarchi
> >>> >> >> >> >
> >>> >> >> >> > <[hidden email]> wrote:
> >>> >> >> >> >> > How often do you start out with a class like this ...
> >>> >> >> >> >>
> >>> >> >> >> >> Never, like I've said. This is the lazy pattern I know since
> >>> >> >> >> >> ever.
> >>> >> >> >> >>
> >>> >> >> >> >> ```js
> >>> >> >> >> >> class Foo {
> >>> >> >> >> >>
> >>> >> >> >> >>   get _db() {
> >>> >> >> >> >>  
> >>> >> >> >> >>     return Object.defineProperty(this, '_db', {
> >>> >> >> >> >>    
> >>> >> >> >> >>       value: new Promise((resolve, reject) => {
> >>> >> >> >> >>      
> >>> >> >> >> >>         // open a database connection
> >>> >> >> >> >>         // set up whatever tables you need to
> >>> >> >> >> >>         // etc.
> >>> >> >> >> >>      
> >>> >> >> >> >>       })
> >>> >> >> >> >>    
> >>> >> >> >> >>     })._db;
> >>> >> >> >> >>  
> >>> >> >> >> >>   }
> >>> >> >> >> >>
> >>> >> >> >> >> }
> >>> >> >> >> >> ```
> >>> >> >> >> >>
> >>> >> >> >> >> Whenever you need, you just access `this._db`, no need to
> >>>
> >>> create
> >>>
> >>> >> >> >> >> an
> >>> >> >> >> >> enumerable variable and a class method.
> >>> >> >> >> >>
> >>> >> >> >> >> It looks cleaner to me.
> >>> >> >> >> >>
> >>> >> >> >> >> > Things you don't want to initialize right away because
> >>> >> >> >> >> > initialization
> >>> >> >> >> >>
> >>> >> >> >> >> You don't really have to convince me, I've written lazy
> >>> >> >> >> >> properties
> >>> >> >> >> >> since
> >>> >> >> >> >> getters and setters were introduced [1]
> >>> >> >> >> >>
> >>> >> >> >> >> All I am saying is that this proposal doesn't compose well
> >>>
> >>> with
> >>>
> >>> >> >> >> >> classes,
> >>> >> >> >> >> it's just yet another SuperPrimitive for the language.
> >>> >> >> >> >>
> >>> >> >> >> >> It is also something trivial to implement on user land, yet
> >>> >> >> >> >> I
> >>> >> >> >> >> haven't
> >>> >> >> >> >> seen
> >>> >> >> >> >> many writing code like the following:
> >>> >> >> >> >>
> >>> >> >> >> >> ```js
> >>> >> >> >> >> function Lazy(fn) {
> >>> >> >> >> >>
> >>> >> >> >> >>   let c = false, v;
> >>> >> >> >> >>   return {get(){ return c ? v : (c = !c, v = fn()) }};
> >>> >> >> >> >>
> >>> >> >> >> >> }
> >>> >> >> >> >>
> >>> >> >> >> >> var o = Lazy(() => Math.random());
> >>> >> >> >> >> o.get(); // ...
> >>> >> >> >> >> ```
> >>> >> >> >> >>
> >>> >> >> >> >> Maybe it's me that hasn't seen this widely adopted from some
> >>> >> >> >> >> library?
> >>> >> >> >> >>
> >>> >> >> >> >> Anyway, this is just my opinion, maybe others would be happy
> >>>
> >>> with
> >>>
> >>> >> >> >> >> this.
> >>> >> >> >> >>
> >>> >> >> >> >> Best Regards
> >>> >> >> >> >>
> >>> >> >> >> >> [1] Class.lazy example
> >>> >> >> >> >>
> >>> >> >> >> >>
> >>> >> >> >> >>
> >>> >> >> >> >> https://github.com/WebReflection/prototypal/blob/master/Clas
> >>>
> >>> s.md#classlazycallback
> >>>
> >>> >> >> >> >> On Thu, Aug 31, 2017 at 6:03 PM, Isiah Meadows
> >>> >> >> >> >> <[hidden email]>
> >>> >> >> >> >>
> >>> >> >> >> >> wrote:
> >>> >> >> >> >>> It'd solve a problem similarly to Kotlin's `by lazy { ...
> >>> >> >> >> >>> }`
> >>> >> >> >> >>> delegate,
> >>> >> >> >> >>> .NET's `System.Lazy<T>`, Swift's `lazy var`, among many
> >>>
> >>> other
> >>>
> >>> >> >> >> >>> languages. It's very useful for lazy initialization [1],
> >>>
> >>> such as
> >>>
> >>> >> >> >> >>> lazily setting up a database, requesting a resource, among
> >>>
> >>> other
> >>>
> >>> >> >> >> >>> costly things. [2]
> >>> >> >> >> >>>
> >>> >> >> >> >>> How often do you start out with a class like this, where
> >>> >> >> >> >>> you
> >>> >> >> >> >>> have
> >>> >> >> >> >>> an
> >>> >> >> >> >>> expensive resource you don't want to open right away?
> >>> >> >> >> >>>
> >>> >> >> >> >>> ```js
> >>> >> >> >> >>> class Foo {
> >>> >> >> >> >>>
> >>> >> >> >> >>>     constructor() {
> >>> >> >> >> >>>    
> >>> >> >> >> >>>         this._db = undefined
> >>> >> >> >> >>>    
> >>> >> >> >> >>>     }
> >>> >> >> >> >>>    
> >>> >> >> >> >>>     _initDb() {
> >>> >> >> >> >>>    
> >>> >> >> >> >>>         if (this._db) return this._db
> >>> >> >> >> >>>         return this._db = new Promise((resolve, reject) =>
> >>> >> >> >> >>>         {
> >>> >> >> >> >>>        
> >>> >> >> >> >>>             // open a database connection
> >>> >> >> >> >>>             // set up whatever tables you need to
> >>> >> >> >> >>>             // etc.
> >>> >> >> >> >>>        
> >>> >> >> >> >>>         })
> >>> >> >> >> >>>    
> >>> >> >> >> >>>     }
> >>> >> >> >> >>>
> >>> >> >> >> >>> }
> >>> >> >> >> >>> ```
> >>> >> >> >> >>>
> >>> >> >> >> >>> Or maybe, a large lookup table that takes a while to build,
> >>>
> >>> and
> >>>
> >>> >> >> >> >>> might
> >>> >> >> >> >>> not even be used, so you don't want to do it on load?
> >>> >> >> >> >>>
> >>> >> >> >> >>> ```js
> >>> >> >> >> >>> var table
> >>> >> >> >> >>>
> >>> >> >> >> >>> function initTable() {
> >>> >> >> >> >>>
> >>> >> >> >> >>>     if (table) return
> >>> >> >> >> >>>     table = new Array(10000)
> >>> >> >> >> >>>     // do some expensive calculations
> >>> >> >> >> >>>
> >>> >> >> >> >>> }
> >>> >> >> >> >>> ```
> >>> >> >> >> >>>
> >>> >> >> >> >>> Things you don't want to initialize right away because
> >>> >> >> >> >>> initialization
> >>> >> >> >> >>> is expensive and/or the value might not even be used.
> >>>
> >>> That's the
> >>>
> >>> >> >> >> >>> problem I'm aiming to solve, and it's something I feel
> >>>
> >>> would be
> >>>
> >>> >> >> >> >>> useful
> >>> >> >> >> >>> in its own right in the language, about equal in importance
> >>>
> >>> to
> >>>
> >>> >> >> >> >>> weak
> >>> >> >> >> >>> references. (Slightly specialized, but the need is not
> >>> >> >> >> >>> non-zero.)
> >>> >> >> >> >>>
> >>> >> >> >> >>> [1]: https://en.wikipedia.org/wiki/Lazy_initialization
> >>> >> >> >> >>> [2]:
> >>> >> >> >> >>>
> >>> >> >> >> >>>
> >>> >> >> >> >>>
> >>> >> >> >> >>> https://stackoverflow.com/ques
> >>>
> >>> tions/978759/what-is-lazy-initialization-and-why-is-it-useful
> >>>
> >>> >> >> >> >>> -----
> >>> >> >> >> >>>
> >>> >> >> >> >>> Isiah Meadows
> >>> >> >> >> >>> [hidden email]
> >>> >> >> >> >>>
> >>> >> >> >> >>> Looking for web consulting? Or a new website?
> >>> >> >> >> >>> Send me an email and we can get started.
> >>> >> >> >> >>> www.isiahmeadows.com
> >>> >> >> >> >>>
> >>> >> >> >> >>>
> >>> >> >> >> >>> On Thu, Aug 31, 2017 at 12:23 PM, Andrea Giammarchi
> >>> >> >> >> >>>
> >>> >> >> >> >>> <[hidden email]> wrote:
> >>> >> >> >> >>> > right ... so ... I'm not sure I understand what this
> >>>
> >>> proposal
> >>>
> >>> >> >> >> >>> > would
> >>> >> >> >> >>> > solve.
> >>> >> >> >> >>> >
> >>> >> >> >> >>> > Instead of this:
> >>> >> >> >> >>> > ```js
> >>> >> >> >> >>> > obj.val || (obj.val = getValue())
> >>> >> >> >> >>> > ```
> >>> >> >> >> >>> >
> >>> >> >> >> >>> > you want to do this
> >>> >> >> >> >>> > ```js
> >>> >> >> >> >>> > (obj.val || (obj.val = new Lazy(getValue)).get();
> >>> >> >> >> >>> > ```
> >>> >> >> >> >>> >
> >>> >> >> >> >>> > Where is the "win" and why is that?
> >>> >> >> >> >>> >
> >>> >> >> >> >>> >
> >>> >> >> >> >>> >
> >>> >> >> >> >>> > On Thu, Aug 31, 2017 at 5:18 PM, Isiah Meadows
> >>> >> >> >> >>> > <[hidden email]>
> >>> >> >> >> >>> >
> >>> >> >> >> >>> > wrote:
> >>> >> >> >> >>> >> With my proposed `Lazy` class, if you were to use an
> >>>
> >>> instance
> >>>
> >>> >> >> >> >>> >> as
> >>> >> >> >> >>> >> a
> >>> >> >> >> >>> >> descriptor, the `this` value it'd receive would not be a
> >>> >> >> >> >>> >> `Lazy`
> >>> >> >> >> >>> >> instance like it'd expect.
> >>> >> >> >> >>> >>
> >>> >> >> >> >>> >> Consider it the difference between `a.self` and
> >>>
> >>> `b.get()` in
> >>>
> >>> >> >> >> >>> >> your
> >>> >> >> >> >>> >> example. `b.get()` is what I'd be expecting.
> >>> >> >> >> >>> >> -----
> >>> >> >> >> >>> >>
> >>> >> >> >> >>> >> Isiah Meadows
> >>> >> >> >> >>> >> [hidden email]
> >>> >> >> >> >>> >>
> >>> >> >> >> >>> >> Looking for web consulting? Or a new website?
> >>> >> >> >> >>> >> Send me an email and we can get started.
> >>> >> >> >> >>> >> www.isiahmeadows.com
> >>> >> >> >> >>> >>
> >>> >> >> >> >>> >>
> >>> >> >> >> >>> >> On Thu, Aug 31, 2017 at 12:12 PM, Andrea Giammarchi
> >>> >> >> >> >>> >>
> >>> >> >> >> >>> >> <[hidden email]> wrote:
> >>> >> >> >> >>> >> >> using it in a descriptor would get it passed the
> >>> >> >> >> >>> >> >> wrong
> >>> >> >> >> >>> >> >> `this`
> >>> >> >> >> >>> >> >
> >>> >> >> >> >>> >> > sorry, what?
> >>> >> >> >> >>> >> >
> >>> >> >> >> >>> >> > ```js
> >>> >> >> >> >>> >> > var a = {};
> >>> >> >> >> >>> >> > var b = {get() { return this; }};
> >>> >> >> >> >>> >> > Object.defineProperty(a, 'self', b);
> >>> >> >> >> >>> >> >
> >>> >> >> >> >>> >> > a.self === a; // true
> >>> >> >> >> >>> >> > ```
> >>> >> >> >> >>> >> >
> >>> >> >> >> >>> >> >
> >>> >> >> >> >>> >> > On Thu, Aug 31, 2017 at 5:09 PM, Isiah Meadows
> >>> >> >> >> >>> >> > <[hidden email]>
> >>> >> >> >> >>> >> >
> >>> >> >> >> >>> >> > wrote:
> >>> >> >> >> >>> >> >> No. `Lazy` is intended to be an object to be used
> >>> >> >> >> >>> >> >> directly,
> >>> >> >> >> >>> >> >> not
> >>> >> >> >> >>> >> >> a
> >>> >> >> >> >>> >> >> descriptor of any kind.
> >>> >> >> >> >>> >> >>
> >>> >> >> >> >>> >> >> (My `lazy.get()` is an unbound method, so using it in
> >>>
> >>> a
> >>>
> >>> >> >> >> >>> >> >> descriptor
> >>> >> >> >> >>> >> >> would get it passed the wrong `this`.)
> >>> >> >> >> >>> >> >> -----
> >>> >> >> >> >>> >> >>
> >>> >> >> >> >>> >> >> Isiah Meadows
> >>> >> >> >> >>> >> >> [hidden email]
> >>> >> >> >> >>> >> >>
> >>> >> >> >> >>> >> >> Looking for web consulting? Or a new website?
> >>> >> >> >> >>> >> >> Send me an email and we can get started.
> >>> >> >> >> >>> >> >> www.isiahmeadows.com
> >>> >> >> >> >>> >> >>
> >>> >> >> >> >>> >> >>
> >>> >> >> >> >>> >> >> On Thu, Aug 31, 2017 at 9:39 AM, Andrea Giammarchi
> >>> >> >> >> >>> >> >>
> >>> >> >> >> >>> >> >> <[hidden email]> wrote:
> >>> >> >> >> >>> >> >> > the following is how I usually consider lazy values
> >>> >> >> >> >>> >> >> >
> >>> >> >> >> >>> >> >> > ```js
> >>> >> >> >> >>> >> >> > class Any {
> >>> >> >> >> >>> >> >> >
> >>> >> >> >> >>> >> >> >   _lazy(name) {
> >>> >> >> >> >>> >> >> >  
> >>> >> >> >> >>> >> >> >     switch (name) {
> >>> >> >> >> >>> >> >> >    
> >>> >> >> >> >>> >> >> >       case 'uid': return Math.random();
> >>> >> >> >> >>> >> >> >       // others ... eventually
> >>> >> >> >> >>> >> >> >    
> >>> >> >> >> >>> >> >> >     }
> >>> >> >> >> >>> >> >> >  
> >>> >> >> >> >>> >> >> >   }
> >>> >> >> >> >>> >> >> >   get uid() {
> >>> >> >> >> >>> >> >> >  
> >>> >> >> >> >>> >> >> >     var value = this._lazy('uid');
> >>> >> >> >> >>> >> >> >     // from now on, direct access
> >>> >> >> >> >>> >> >> >     Object.defineProperty(this, 'uid', {value});
> >>> >> >> >> >>> >> >> >     return value;
> >>> >> >> >> >>> >> >> >  
> >>> >> >> >> >>> >> >> >   }
> >>> >> >> >> >>> >> >> >
> >>> >> >> >> >>> >> >> > }
> >>> >> >> >> >>> >> >> >
> >>> >> >> >> >>> >> >> > const a = new Any;
> >>> >> >> >> >>> >> >> > a.uid === a.uid; // true
> >>> >> >> >> >>> >> >> > ```
> >>> >> >> >> >>> >> >> >
> >>> >> >> >> >>> >> >> > If I understand correctly your proposal is to use
> >>>
> >>> Lazy
> >>>
> >>> >> >> >> >>> >> >> > as
> >>> >> >> >> >>> >> >> > generic
> >>> >> >> >> >>> >> >> > descriptor, is that correct ?
> >>> >> >> >> >>> >> >> >
> >>> >> >> >> >>> >> >> > ```js
> >>> >> >> >> >>> >> >> > Object.defineProperty({}, 'something', new
> >>>
> >>> Lazy(function
> >>>
> >>> >> >> >> >>> >> >> > (val)
> >>> >> >> >> >>> >> >> > {
> >>> >> >> >> >>> >> >> >
> >>> >> >> >> >>> >> >> >   return this.shakaLaka ? val : 'no shakaLaka';
> >>> >> >> >> >>> >> >> >
> >>> >> >> >> >>> >> >> > }));
> >>> >> >> >> >>> >> >> > ```
> >>> >> >> >> >>> >> >> >
> >>> >> >> >> >>> >> >> > ???
> >>> >> >> >> >>> >> >> >
> >>> >> >> >> >>> >> >> > If that's the case I see already people confused by
> >>> >> >> >> >>> >> >> > arrow
> >>> >> >> >> >>> >> >> > function
> >>> >> >> >> >>> >> >> > in case they need to access the context,
> >>> >> >> >> >>> >> >> > plus no property access optimization once resolved.
> >>> >> >> >> >>> >> >> >
> >>> >> >> >> >>> >> >> > It's also not clear if such property can be set
> >>>
> >>> again
> >>>
> >>> >> >> >> >>> >> >> > later
> >>> >> >> >> >>> >> >> > on
> >>> >> >> >> >>> >> >> > (right
> >>> >> >> >> >>> >> >> > now it
> >>> >> >> >> >>> >> >> > cannot)
> >>> >> >> >> >>> >> >> > 'cause lazy definition doesn't always necessarily
> >>>
> >>> mean
> >>>
> >>> >> >> >> >>> >> >> > inability
> >>> >> >> >> >>> >> >> > to
> >>> >> >> >> >>> >> >> > reassign.
> >>> >> >> >> >>> >> >> >
> >>> >> >> >> >>> >> >> > What am I missing/misunderstanding?
> >>> >> >> >> >>> >> >> >
> >>> >> >> >> >>> >> >> > Regards
> >>> >> >> >> >>> >> >> >
> >>> >> >> >> >>> >> >> >
> >>> >> >> >> >>> >> >> >
> >>> >> >> >> >>> >> >> > On Thu, Aug 31, 2017 at 2:21 PM, Isiah Meadows
> >>> >> >> >> >>> >> >> > <[hidden email]>
> >>> >> >> >> >>> >> >> >
> >>> >> >> >> >>> >> >> > wrote:
> >>> >> >> >> >>> >> >> >> It'd be really nice if lazy values made it into
> >>> >> >> >> >>> >> >> >> the
> >>> >> >> >> >>> >> >> >> spec
> >>> >> >> >> >>> >> >> >> somehow.
> >>> >> >> >> >>> >> >> >> I've
> >>> >> >> >> >>> >> >> >> already found myself using things like this [1]
> >>>
> >>> quite a
> >>>
> >>> >> >> >> >>> >> >> >> bit,
> >>> >> >> >> >>> >> >> >> and
> >>> >> >> >> >>> >> >> >> I've
> >>> >> >> >> >>> >> >> >> also found myself frequently initializing
> >>>
> >>> properties
> >>>
> >>> >> >> >> >>> >> >> >> not
> >>> >> >> >> >>> >> >> >> on
> >>> >> >> >> >>> >> >> >> first
> >>> >> >> >> >>> >> >> >> access.
> >>> >> >> >> >>> >> >> >>
> >>> >> >> >> >>> >> >> >> [1]:
> >>> >> >> >> >>> >> >> >>
> >>> >> >> >> >>> >> >> >>
> >>> >> >> >> >>> >> >> >>
> >>> >> >> >> >>> >> >> >>
> >>> >> >> >> >>> >> >> >>
> >>> >> >> >> >>> >> >> >> https://gist.github.com/isiahm
> >>>
> >>> eadows/4c0723bdfa555a1c2cb01341b323c3d4
> >>>
> >>> >> >> >> >>> >> >> >> As for what would be a nice API, maybe something
> >>>
> >>> like
> >>>
> >>> >> >> >> >>> >> >> >> one
> >>> >> >> >> >>> >> >> >> of
> >>> >> >> >> >>> >> >> >> these?
> >>> >> >> >> >>> >> >> >>
> >>> >> >> >> >>> >> >> >> ```js
> >>> >> >> >> >>> >> >> >> class Lazy<T> {
> >>> >> >> >> >>> >> >> >>
> >>> >> >> >> >>> >> >> >>     constructor(init: () => T);
> >>> >> >> >> >>> >> >> >>     get(): T; // or error thrown
> >>> >> >> >> >>> >> >> >>
> >>> >> >> >> >>> >> >> >> }
> >>> >> >> >> >>> >> >> >>
> >>> >> >> >> >>> >> >> >> function lazy<T>(init: () => T): () => T; // or
> >>>
> >>> error
> >>>
> >>> >> >> >> >>> >> >> >> thrown
> >>> >> >> >> >>> >> >> >>
> >>> >> >> >> >>> >> >> >> function lazy<T>(init: () => T): {
> >>> >> >> >> >>> >> >> >>
> >>> >> >> >> >>> >> >> >>     get(): T; // or error thrown
> >>> >> >> >> >>> >> >> >>
> >>> >> >> >> >>> >> >> >> }
> >>> >> >> >> >>> >> >> >> ```
> >>> >> >> >> >>> >> >> >>
> >>> >> >> >> >>> >> >> >> Alternatively, syntax might work, with `do`
> >>>
> >>> expression
> >>>
> >>> >> >> >> >>> >> >> >> semantics:
> >>> >> >> >> >>> >> >> >>
> >>> >> >> >> >>> >> >> >> ```js
> >>> >> >> >> >>> >> >> >> const x = lazy do { ... }
> >>> >> >> >> >>> >> >> >> // expose via `x.get()` or just `x()`
> >>> >> >> >> >>> >> >> >> ```
> >>> >> >> >> >>> >> >> >>
> >>> >> >> >> >>> >> >> >> -----
> >>> >> >> >> >>> >> >> >>
> >>> >> >> >> >>> >> >> >> Isiah Meadows
> >>> >> >> >> >>> >> >> >> [hidden email]
> >>> >> >> >> >>> >> >> >>
> >>> >> >> >> >>> >> >> >> Looking for web consulting? Or a new website?
> >>> >> >> >> >>> >> >> >> Send me an email and we can get started.
> >>> >> >> >> >>> >> >> >> www.isiahmeadows.com
> >>> >> >> >> >>> >> >> >> _______________________________________________
> >>> >> >> >> >>> >> >> >> es-discuss mailing list
> >>> >> >> >> >>> >> >> >> [hidden email]
> >>> >> >> >> >>> >> >> >> https://mail.mozilla.org/listinfo/es-discuss
> >>> >> >>
> >>> >> >> -----
> >>> >> >>
> >>> >> >> Isiah Meadows
> >>> >> >> [hidden email]
> >>> >> >>
> >>> >> >> Looking for web consulting? Or a new website?
> >>> >> >> Send me an email and we can get started.
> >>> >> >> www.isiahmeadows.com
> >>
> >> _______________________________________________
> >> 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

signature.asc (849 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Lazy evaluation

Andrea Giammarchi-2
In reply to this post by Michał Wadas
then yes, decorators solve my use case pretty well.

```js
const lazy = (Class, prop, desc) => {
  var get = desc.get;
  desc.get = function () {
    var result = get.apply(this, arguments);
    Object.defineProperty(this, prop, {value: result});
    return result;
  };
};

class A {
  @lazy
  get random() { return Math.random(); }
}

let a = new A;
a.random === a.random; // true
```

On Fri, Sep 1, 2017 at 8:16 AM, Michał Wadas <[hidden email]> wrote:
Stage 2, but they move really slow. 

On 1 Sep 2017 9:15 am, "Andrea Giammarchi" <[hidden email]> wrote:
I thought decorators were nowhere higher than stage 0 (since ever)

On Thu, Aug 31, 2017 at 9:53 PM, Michał Wadas <[hidden email]> wrote:
Why not something like decorators (not sure if decorator  proposal covers this already)? 

class Foo {
@cached
get bar() {
return something(this);

On 31 Aug 2017 10:30 pm, "Andrea Giammarchi" <[hidden email]> wrote:
it's a matter of semantics.

If I see this

```js
var later = anyWrappingName(() => Math.random());

// this is an assumption, not something obvious
later() === later()
```

If instead, I write this:
```js
this.later === this.later;
```

I expect that to never possibly fail like `arr.length === arr.length` or any `obj.prop`, in APIs with common sense, are equal to `obj.prop`.

Invokes via instances and objects? It's never obvious at first look, if that is a method execution, but it's surely a new invoke.

If you've trapped once the result behind the scene, reading that, is just noise for anyone eyes.

So, once again, are we proposing something that results into exactly this?

```js
class Later {
  get thing() {
    return Object.defineProperty(this, 'thing', {value: anyLazy()});
  }
  constructor() {
    // always true, no matter when/where
    this.thing === this.thing;
  }
}
```

If so, I'm happy. If not, this is confusing and solving not much.


Best Regards


On Thu, Aug 31, 2017 at 9:14 PM, Isiah Meadows <[hidden email]> wrote:
Yes. I'll point out that having it as a function, rather than a
property-specific thing, makes it more flexible, since you can define
constants as lazy values (I do that in quite a few places).

If you want to make it transparent, it's not that hard to make a
single-line getter/method that hides the abstraction.

Granted, most of my lazy values are properties, not constants, so I
could consider it an acceptable compromise.
-----

Isiah Meadows
[hidden email]

Looking for web consulting? Or a new website?
Send me an email and we can get started.
www.isiahmeadows.com


On Thu, Aug 31, 2017 at 3:54 PM, Andrea Giammarchi
<[hidden email]> wrote:
> so in JavaScript that results into this._db() each time, resolved lazily
> with the first value returned once ?
>
> I still think my approach is cleaner and more transparent.
>
> `get _thing() { return defineProperty(this, 'thing', value) }`
>
> but if your TS-ish stuff translates into that, works for me
>
>
>
> On Thu, Aug 31, 2017 at 8:49 PM, Isiah Meadows <[hidden email]>
> wrote:
>>
>> It takes a function, and returns a function that (if necessary)
>> initializes the value and then gets it.
>> -----
>>
>> Isiah Meadows
>> [hidden email]
>>
>> Looking for web consulting? Or a new website?
>> Send me an email and we can get started.
>> www.isiahmeadows.com
>>
>>
>> On Thu, Aug 31, 2017 at 3:43 PM, Andrea Giammarchi
>> <[hidden email]> wrote:
>> > Sorry I don't speak TS, I speak ES.
>> >
>> > Can you please tell me in JavaScript what does that do?
>> >
>> > On Thu, Aug 31, 2017 at 8:18 PM, Isiah Meadows <[hidden email]>
>> > wrote:
>> >>
>> >> Note the TS-ish declaration above it. That's the variant I was
>> >> referring to (I presented about 3 different variants initially).
>> >>
>> >> ```ts
>> >> // The declaration I included
>> >> declare function lazy<T>(init: () => T): () => T;
>> >> ```
>> >>
>> >>
>> >> On Thu, Aug 31, 2017 at 3:05 PM, Andrea Giammarchi
>> >> <[hidden email]> wrote:
>> >> > it wouldn't work, would it ? I mean, you still have to pass through
>> >> > the
>> >> > "ugly" _db.get() thingy, right?
>> >> >
>> >> > how do you access and trigger the lazy bit within the class?
>> >> >
>> >> > On Thu, Aug 31, 2017 at 7:56 PM, Isiah Meadows
>> >> > <[hidden email]>
>> >> > wrote:
>> >> >>
>> >> >> What about this (using the stage 3 class fields proposal)?
>> >> >>
>> >> >> ```js
>> >> >> declare function lazy<T>(init: () => T): () => T;
>> >> >>
>> >> >> class WithLazyVals {
>> >> >>     _db = lazy(() => new Promise(...));
>> >> >> }
>> >> >> ```
>> >> >> -----
>> >> >>
>> >> >> Isiah Meadows
>> >> >> [hidden email]
>> >> >>
>> >> >> Looking for web consulting? Or a new website?
>> >> >> Send me an email and we can get started.
>> >> >> www.isiahmeadows.com
>> >> >>
>> >> >>
>> >> >> On Thu, Aug 31, 2017 at 1:34 PM, Andrea Giammarchi
>> >> >> <[hidden email]> wrote:
>> >> >> >> this proposal doesn't compose well with classes
>> >> >> >
>> >> >> > to expand a little, if you were proposing
>> >> >> >
>> >> >> > ```js
>> >> >> > class WithLazyVals {
>> >> >> >   lazy _db() { return new Promise(...); }
>> >> >> > }
>> >> >> > ```
>> >> >> >
>> >> >> > I would've taken first flight to come over and hug you.
>> >> >> >
>> >> >> > Best Regards
>> >> >> >
>> >> >> >
>> >> >> >
>> >> >> >
>> >> >> > On Thu, Aug 31, 2017 at 6:25 PM, Andrea Giammarchi
>> >> >> > <[hidden email]> wrote:
>> >> >> >>
>> >> >> >> > How often do you start out with a class like this ...
>> >> >> >>
>> >> >> >> Never, like I've said. This is the lazy pattern I know since
>> >> >> >> ever.
>> >> >> >>
>> >> >> >> ```js
>> >> >> >> class Foo {
>> >> >> >>   get _db() {
>> >> >> >>     return Object.defineProperty(this, '_db', {
>> >> >> >>       value: new Promise((resolve, reject) => {
>> >> >> >>         // open a database connection
>> >> >> >>         // set up whatever tables you need to
>> >> >> >>         // etc.
>> >> >> >>       })
>> >> >> >>     })._db;
>> >> >> >>   }
>> >> >> >> }
>> >> >> >> ```
>> >> >> >>
>> >> >> >> Whenever you need, you just access `this._db`, no need to create
>> >> >> >> an
>> >> >> >> enumerable variable and a class method.
>> >> >> >>
>> >> >> >> It looks cleaner to me.
>> >> >> >>
>> >> >> >>
>> >> >> >> > Things you don't want to initialize right away because
>> >> >> >> > initialization
>> >> >> >>
>> >> >> >> You don't really have to convince me, I've written lazy
>> >> >> >> properties
>> >> >> >> since
>> >> >> >> getters and setters were introduced [1]
>> >> >> >>
>> >> >> >> All I am saying is that this proposal doesn't compose well with
>> >> >> >> classes,
>> >> >> >> it's just yet another SuperPrimitive for the language.
>> >> >> >>
>> >> >> >> It is also something trivial to implement on user land, yet I
>> >> >> >> haven't
>> >> >> >> seen
>> >> >> >> many writing code like the following:
>> >> >> >>
>> >> >> >> ```js
>> >> >> >> function Lazy(fn) {
>> >> >> >>   let c = false, v;
>> >> >> >>   return {get(){ return c ? v : (c = !c, v = fn()) }};
>> >> >> >> }
>> >> >> >>
>> >> >> >> var o = Lazy(() => Math.random());
>> >> >> >> o.get(); // ...
>> >> >> >> ```
>> >> >> >>
>> >> >> >> Maybe it's me that hasn't seen this widely adopted from some
>> >> >> >> library?
>> >> >> >>
>> >> >> >> Anyway, this is just my opinion, maybe others would be happy with
>> >> >> >> this.
>> >> >> >>
>> >> >> >> Best Regards
>> >> >> >>
>> >> >> >> [1] Class.lazy example
>> >> >> >>
>> >> >> >>
>> >> >> >>
>> >> >> >> https://github.com/WebReflection/prototypal/blob/master/Class.md#classlazycallback
>> >> >> >>
>> >> >> >>
>> >> >> >>
>> >> >> >> On Thu, Aug 31, 2017 at 6:03 PM, Isiah Meadows
>> >> >> >> <[hidden email]>
>> >> >> >> wrote:
>> >> >> >>>
>> >> >> >>> It'd solve a problem similarly to Kotlin's `by lazy { ... }`
>> >> >> >>> delegate,
>> >> >> >>> .NET's `System.Lazy<T>`, Swift's `lazy var`, among many other
>> >> >> >>> languages. It's very useful for lazy initialization [1], such as
>> >> >> >>> lazily setting up a database, requesting a resource, among other
>> >> >> >>> costly things. [2]
>> >> >> >>>
>> >> >> >>> How often do you start out with a class like this, where you
>> >> >> >>> have
>> >> >> >>> an
>> >> >> >>> expensive resource you don't want to open right away?
>> >> >> >>>
>> >> >> >>> ```js
>> >> >> >>> class Foo {
>> >> >> >>>     constructor() {
>> >> >> >>>         this._db = undefined
>> >> >> >>>     }
>> >> >> >>>
>> >> >> >>>     _initDb() {
>> >> >> >>>         if (this._db) return this._db
>> >> >> >>>         return this._db = new Promise((resolve, reject) => {
>> >> >> >>>             // open a database connection
>> >> >> >>>             // set up whatever tables you need to
>> >> >> >>>             // etc.
>> >> >> >>>         })
>> >> >> >>>     }
>> >> >> >>> }
>> >> >> >>> ```
>> >> >> >>>
>> >> >> >>> Or maybe, a large lookup table that takes a while to build, and
>> >> >> >>> might
>> >> >> >>> not even be used, so you don't want to do it on load?
>> >> >> >>>
>> >> >> >>> ```js
>> >> >> >>> var table
>> >> >> >>>
>> >> >> >>> function initTable() {
>> >> >> >>>     if (table) return
>> >> >> >>>     table = new Array(10000)
>> >> >> >>>     // do some expensive calculations
>> >> >> >>> }
>> >> >> >>> ```
>> >> >> >>>
>> >> >> >>> Things you don't want to initialize right away because
>> >> >> >>> initialization
>> >> >> >>> is expensive and/or the value might not even be used. That's the
>> >> >> >>> problem I'm aiming to solve, and it's something I feel would be
>> >> >> >>> useful
>> >> >> >>> in its own right in the language, about equal in importance to
>> >> >> >>> weak
>> >> >> >>> references. (Slightly specialized, but the need is not
>> >> >> >>> non-zero.)
>> >> >> >>>
>> >> >> >>> [1]: https://en.wikipedia.org/wiki/Lazy_initialization
>> >> >> >>> [2]:
>> >> >> >>>
>> >> >> >>>
>> >> >> >>>
>> >> >> >>> https://stackoverflow.com/questions/978759/what-is-lazy-initialization-and-why-is-it-useful
>> >> >> >>> -----
>> >> >> >>>
>> >> >> >>> Isiah Meadows
>> >> >> >>> [hidden email]
>> >> >> >>>
>> >> >> >>> Looking for web consulting? Or a new website?
>> >> >> >>> Send me an email and we can get started.
>> >> >> >>> www.isiahmeadows.com
>> >> >> >>>
>> >> >> >>>
>> >> >> >>> On Thu, Aug 31, 2017 at 12:23 PM, Andrea Giammarchi
>> >> >> >>> <[hidden email]> wrote:
>> >> >> >>> > right ... so ... I'm not sure I understand what this proposal
>> >> >> >>> > would
>> >> >> >>> > solve.
>> >> >> >>> >
>> >> >> >>> > Instead of this:
>> >> >> >>> > ```js
>> >> >> >>> > obj.val || (obj.val = getValue())
>> >> >> >>> > ```
>> >> >> >>> >
>> >> >> >>> > you want to do this
>> >> >> >>> > ```js
>> >> >> >>> > (obj.val || (obj.val = new Lazy(getValue)).get();
>> >> >> >>> > ```
>> >> >> >>> >
>> >> >> >>> > Where is the "win" and why is that?
>> >> >> >>> >
>> >> >> >>> >
>> >> >> >>> >
>> >> >> >>> > On Thu, Aug 31, 2017 at 5:18 PM, Isiah Meadows
>> >> >> >>> > <[hidden email]>
>> >> >> >>> > wrote:
>> >> >> >>> >>
>> >> >> >>> >> With my proposed `Lazy` class, if you were to use an instance
>> >> >> >>> >> as
>> >> >> >>> >> a
>> >> >> >>> >> descriptor, the `this` value it'd receive would not be a
>> >> >> >>> >> `Lazy`
>> >> >> >>> >> instance like it'd expect.
>> >> >> >>> >>
>> >> >> >>> >> Consider it the difference between `a.self` and `b.get()` in
>> >> >> >>> >> your
>> >> >> >>> >> example. `b.get()` is what I'd be expecting.
>> >> >> >>> >> -----
>> >> >> >>> >>
>> >> >> >>> >> Isiah Meadows
>> >> >> >>> >> [hidden email]
>> >> >> >>> >>
>> >> >> >>> >> Looking for web consulting? Or a new website?
>> >> >> >>> >> Send me an email and we can get started.
>> >> >> >>> >> www.isiahmeadows.com
>> >> >> >>> >>
>> >> >> >>> >>
>> >> >> >>> >> On Thu, Aug 31, 2017 at 12:12 PM, Andrea Giammarchi
>> >> >> >>> >> <[hidden email]> wrote:
>> >> >> >>> >> >> using it in a descriptor would get it passed the wrong
>> >> >> >>> >> >> `this`
>> >> >> >>> >> >
>> >> >> >>> >> > sorry, what?
>> >> >> >>> >> >
>> >> >> >>> >> > ```js
>> >> >> >>> >> > var a = {};
>> >> >> >>> >> > var b = {get() { return this; }};
>> >> >> >>> >> > Object.defineProperty(a, 'self', b);
>> >> >> >>> >> >
>> >> >> >>> >> > a.self === a; // true
>> >> >> >>> >> > ```
>> >> >> >>> >> >
>> >> >> >>> >> >
>> >> >> >>> >> > On Thu, Aug 31, 2017 at 5:09 PM, Isiah Meadows
>> >> >> >>> >> > <[hidden email]>
>> >> >> >>> >> > wrote:
>> >> >> >>> >> >>
>> >> >> >>> >> >> No. `Lazy` is intended to be an object to be used
>> >> >> >>> >> >> directly,
>> >> >> >>> >> >> not
>> >> >> >>> >> >> a
>> >> >> >>> >> >> descriptor of any kind.
>> >> >> >>> >> >>
>> >> >> >>> >> >> (My `lazy.get()` is an unbound method, so using it in a
>> >> >> >>> >> >> descriptor
>> >> >> >>> >> >> would get it passed the wrong `this`.)
>> >> >> >>> >> >> -----
>> >> >> >>> >> >>
>> >> >> >>> >> >> Isiah Meadows
>> >> >> >>> >> >> [hidden email]
>> >> >> >>> >> >>
>> >> >> >>> >> >> Looking for web consulting? Or a new website?
>> >> >> >>> >> >> Send me an email and we can get started.
>> >> >> >>> >> >> www.isiahmeadows.com
>> >> >> >>> >> >>
>> >> >> >>> >> >>
>> >> >> >>> >> >> On Thu, Aug 31, 2017 at 9:39 AM, Andrea Giammarchi
>> >> >> >>> >> >> <[hidden email]> wrote:
>> >> >> >>> >> >> > the following is how I usually consider lazy values
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > ```js
>> >> >> >>> >> >> > class Any {
>> >> >> >>> >> >> >   _lazy(name) {
>> >> >> >>> >> >> >     switch (name) {
>> >> >> >>> >> >> >       case 'uid': return Math.random();
>> >> >> >>> >> >> >       // others ... eventually
>> >> >> >>> >> >> >     }
>> >> >> >>> >> >> >   }
>> >> >> >>> >> >> >   get uid() {
>> >> >> >>> >> >> >     var value = this._lazy('uid');
>> >> >> >>> >> >> >     // from now on, direct access
>> >> >> >>> >> >> >     Object.defineProperty(this, 'uid', {value});
>> >> >> >>> >> >> >     return value;
>> >> >> >>> >> >> >   }
>> >> >> >>> >> >> > }
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > const a = new Any;
>> >> >> >>> >> >> > a.uid === a.uid; // true
>> >> >> >>> >> >> > ```
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > If I understand correctly your proposal is to use Lazy
>> >> >> >>> >> >> > as
>> >> >> >>> >> >> > generic
>> >> >> >>> >> >> > descriptor, is that correct ?
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > ```js
>> >> >> >>> >> >> > Object.defineProperty({}, 'something', new Lazy(function
>> >> >> >>> >> >> > (val)
>> >> >> >>> >> >> > {
>> >> >> >>> >> >> >   return this.shakaLaka ? val : 'no shakaLaka';
>> >> >> >>> >> >> > }));
>> >> >> >>> >> >> > ```
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > ???
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > If that's the case I see already people confused by
>> >> >> >>> >> >> > arrow
>> >> >> >>> >> >> > function
>> >> >> >>> >> >> > in case they need to access the context,
>> >> >> >>> >> >> > plus no property access optimization once resolved.
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > It's also not clear if such property can be set again
>> >> >> >>> >> >> > later
>> >> >> >>> >> >> > on
>> >> >> >>> >> >> > (right
>> >> >> >>> >> >> > now it
>> >> >> >>> >> >> > cannot)
>> >> >> >>> >> >> > 'cause lazy definition doesn't always necessarily mean
>> >> >> >>> >> >> > inability
>> >> >> >>> >> >> > to
>> >> >> >>> >> >> > reassign.
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > What am I missing/misunderstanding?
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > Regards
>> >> >> >>> >> >> >
>> >> >> >>> >> >> >
>> >> >> >>> >> >> >
>> >> >> >>> >> >> > On Thu, Aug 31, 2017 at 2:21 PM, Isiah Meadows
>> >> >> >>> >> >> > <[hidden email]>
>> >> >> >>> >> >> > wrote:
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> It'd be really nice if lazy values made it into the
>> >> >> >>> >> >> >> spec
>> >> >> >>> >> >> >> somehow.
>> >> >> >>> >> >> >> I've
>> >> >> >>> >> >> >> already found myself using things like this [1] quite a
>> >> >> >>> >> >> >> bit,
>> >> >> >>> >> >> >> and
>> >> >> >>> >> >> >> I've
>> >> >> >>> >> >> >> also found myself frequently initializing properties
>> >> >> >>> >> >> >> not
>> >> >> >>> >> >> >> on
>> >> >> >>> >> >> >> first
>> >> >> >>> >> >> >> access.
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> [1]:
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> https://gist.github.com/isiahmeadows/4c0723bdfa555a1c2cb01341b323c3d4
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> As for what would be a nice API, maybe something like
>> >> >> >>> >> >> >> one
>> >> >> >>> >> >> >> of
>> >> >> >>> >> >> >> these?
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> ```js
>> >> >> >>> >> >> >> class Lazy<T> {
>> >> >> >>> >> >> >>     constructor(init: () => T);
>> >> >> >>> >> >> >>     get(): T; // or error thrown
>> >> >> >>> >> >> >> }
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> function lazy<T>(init: () => T): () => T; // or error
>> >> >> >>> >> >> >> thrown
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> function lazy<T>(init: () => T): {
>> >> >> >>> >> >> >>     get(): T; // or error thrown
>> >> >> >>> >> >> >> }
>> >> >> >>> >> >> >> ```
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> Alternatively, syntax might work, with `do` expression
>> >> >> >>> >> >> >> semantics:
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> ```js
>> >> >> >>> >> >> >> const x = lazy do { ... }
>> >> >> >>> >> >> >> // expose via `x.get()` or just `x()`
>> >> >> >>> >> >> >> ```
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> -----
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> Isiah Meadows
>> >> >> >>> >> >> >> [hidden email]
>> >> >> >>> >> >> >>
>> >> >> >>> >> >> >> Looking for web consulting? Or a new website?
>> >> >> >>> >> >> >> Send me an email and we can get started.
>> >> >> >>> >> >> >> www.isiahmeadows.com
>> >> >> >>> >> >> >> _______________________________________________
>> >> >> >>> >> >> >> es-discuss mailing list
>> >> >> >>> >> >> >> [hidden email]
>> >> >> >>> >> >> >> https://mail.mozilla.org/listinfo/es-discuss
>> >> >> >>> >> >> >
>> >> >> >>> >> >> >
>> >> >> >>> >> >
>> >> >> >>> >> >
>> >> >> >>> >
>> >> >> >>> >
>> >> >> >>
>> >> >> >>
>> >> >> >
>> >> >
>> >> >
>> >>
>> >> -----
>> >>
>> >> Isiah Meadows
>> >> [hidden email]
>> >>
>> >> Looking for web consulting? Or a new website?
>> >> Send me an email and we can get started.
>> >> www.isiahmeadows.com
>> >
>> >
>
>


_______________________________________________
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: Lazy evaluation

Herby Vojčík
In reply to this post by Isiah Meadows-2


On August 31, 2017 6:15:20 PM GMT+02:00, Isiah Meadows <[hidden email]> wrote:
>Promises are inherently eager, but also async - consider that `new
>Promise(resolve => resolve(1))` is roughly equivalent to `var promise
>= Promise.resolve(1)`.
>
>My proposal is for a single immediate value, but created on demand
>(when you call `.get()`) rather than immediately.

But then, it seems to me Andrea's self-rewriting getter gets to the point.

Maybe there can be 'caching' getter planned as in:

Object.defineProperty(foo, "bar", {
  get: () => "baz",
  caching: true
});

with a shortcut

Object.defineLazyValue(
  foo, "bar", () => "baz");

which is implementable via a lib.

Herby

>-----
>
>Isiah Meadows
>[hidden email]
>
>Looking for web consulting? Or a new website?
>Send me an email and we can get started.
>www.isiahmeadows.com
>
>
>On Thu, Aug 31, 2017 at 10:37 AM, Naveen Chawla <[hidden email]>
>wrote:
>> Could you not do this with a promise? If not, what's missing in
>promise that
>> you could do with "lazy"? Sorry if I've missed the whole premise
>>
>> On Thu, 31 Aug 2017 at 19:09 Andrea Giammarchi
><[hidden email]>
>> wrote:
>>>
>>> the following is how I usually consider lazy values
>>>
>>> ```js
>>> class Any {
>>>   _lazy(name) {
>>>     switch (name) {
>>>       case 'uid': return Math.random();
>>>       // others ... eventually
>>>     }
>>>   }
>>>   get uid() {
>>>     var value = this._lazy('uid');
>>>     // from now on, direct access
>>>     Object.defineProperty(this, 'uid', {value});
>>>     return value;
>>>   }
>>> }
>>>
>>> const a = new Any;
>>> a.uid === a.uid; // true
>>> ```
>>>
>>> If I understand correctly your proposal is to use Lazy as generic
>>> descriptor, is that correct ?
>>>
>>> ```js
>>> Object.defineProperty({}, 'something', new Lazy(function (val) {
>>>   return this.shakaLaka ? val : 'no shakaLaka';
>>> }));
>>> ```
>>>
>>> ???
>>>
>>> If that's the case I see already people confused by arrow function
>>> in case they need to access the context,
>>> plus no property access optimization once resolved.
>>>
>>> It's also not clear if such property can be set again later on
>(right now
>>> it cannot)
>>> 'cause lazy definition doesn't always necessarily mean inability to
>>> reassign.
>>>
>>> What am I missing/misunderstanding?
>>>
>>> Regards
>>>
>>>
>>>
>>> On Thu, Aug 31, 2017 at 2:21 PM, Isiah Meadows
><[hidden email]>
>>> wrote:
>>>>
>>>> It'd be really nice if lazy values made it into the spec somehow.
>I've
>>>> already found myself using things like this [1] quite a bit, and
>I've
>>>> also found myself frequently initializing properties not on first
>>>> access.
>>>>
>>>> [1]:
>>>>
>https://gist.github.com/isiahmeadows/4c0723bdfa555a1c2cb01341b323c3d4
>>>>
>>>> As for what would be a nice API, maybe something like one of these?
>>>>
>>>> ```js
>>>> class Lazy<T> {
>>>>     constructor(init: () => T);
>>>>     get(): T; // or error thrown
>>>> }
>>>>
>>>> function lazy<T>(init: () => T): () => T; // or error thrown
>>>>
>>>> function lazy<T>(init: () => T): {
>>>>     get(): T; // or error thrown
>>>> }
>>>> ```
>>>>
>>>> Alternatively, syntax might work, with `do` expression semantics:
>>>>
>>>> ```js
>>>> const x = lazy do { ... }
>>>> // expose via `x.get()` or just `x()`
>>>> ```
>>>>
>>>> -----
>>>>
>>>> Isiah Meadows
>>>> [hidden email]
>>>>
>>>> Looking for web consulting? Or a new website?
>>>> Send me an email and we can get started.
>>>> www.isiahmeadows.com
>>>> _______________________________________________
>>>> 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
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Lazy evaluation

Michał Wadas
BTW consider this:

```js
class Foo {
  get bar() {
    Object.defineProperty(this, 'bar', {value: []});
    return this.bar;
  }
}
const a = new Foo;
Foo.prototype.bar;
const b = new Foo;
const c = new Foo;
// b.bar === c.bar
```

So I recommend this instead:

```js
const wm = new WeakMap;
class Foo {
  get bar() {
    if (!wm.has(this)) {
      wm.set(this, []);
    }
    return wm.get(this);
  }
}
const a = new Foo;
Foo.prototype.bar;
const b = new Foo;
const c = new Foo;
// b.bar !== c.bar
```


On Fri, Sep 1, 2017 at 12:11 PM, <[hidden email]> wrote:


On August 31, 2017 6:15:20 PM GMT+02:00, Isiah Meadows <[hidden email]> wrote:
>Promises are inherently eager, but also async - consider that `new
>Promise(resolve => resolve(1))` is roughly equivalent to `var promise
>= Promise.resolve(1)`.
>
>My proposal is for a single immediate value, but created on demand
>(when you call `.get()`) rather than immediately.

But then, it seems to me Andrea's self-rewriting getter gets to the point.

Maybe there can be 'caching' getter planned as in:

Object.defineProperty(foo, "bar", {
  get: () => "baz",
  caching: true
});

with a shortcut

Object.defineLazyValue(
  foo, "bar", () => "baz");

which is implementable via a lib.

Herby
>-----
>
>Isiah Meadows
>[hidden email]
>
>Looking for web consulting? Or a new website?
>Send me an email and we can get started.
>www.isiahmeadows.com
>
>
>On Thu, Aug 31, 2017 at 10:37 AM, Naveen Chawla <[hidden email]>
>wrote:
>> Could you not do this with a promise? If not, what's missing in
>promise that
>> you could do with "lazy"? Sorry if I've missed the whole premise
>>
>> On Thu, 31 Aug 2017 at 19:09 Andrea Giammarchi
><[hidden email]>
>> wrote:
>>>
>>> the following is how I usually consider lazy values
>>>
>>> ```js
>>> class Any {
>>>   _lazy(name) {
>>>     switch (name) {
>>>       case 'uid': return Math.random();
>>>       // others ... eventually
>>>     }
>>>   }
>>>   get uid() {
>>>     var value = this._lazy('uid');
>>>     // from now on, direct access
>>>     Object.defineProperty(this, 'uid', {value});
>>>     return value;
>>>   }
>>> }
>>>
>>> const a = new Any;
>>> a.uid === a.uid; // true
>>> ```
>>>
>>> If I understand correctly your proposal is to use Lazy as generic
>>> descriptor, is that correct ?
>>>
>>> ```js
>>> Object.defineProperty({}, 'something', new Lazy(function (val) {
>>>   return this.shakaLaka ? val : 'no shakaLaka';
>>> }));
>>> ```
>>>
>>> ???
>>>
>>> If that's the case I see already people confused by arrow function
>>> in case they need to access the context,
>>> plus no property access optimization once resolved.
>>>
>>> It's also not clear if such property can be set again later on
>(right now
>>> it cannot)
>>> 'cause lazy definition doesn't always necessarily mean inability to
>>> reassign.
>>>
>>> What am I missing/misunderstanding?
>>>
>>> Regards
>>>
>>>
>>>
>>> On Thu, Aug 31, 2017 at 2:21 PM, Isiah Meadows
><[hidden email]>
>>> wrote:
>>>>
>>>> It'd be really nice if lazy values made it into the spec somehow.
>I've
>>>> already found myself using things like this [1] quite a bit, and
>I've
>>>> also found myself frequently initializing properties not on first
>>>> access.
>>>>
>>>> [1]:
>>>>
>https://gist.github.com/isiahmeadows/4c0723bdfa555a1c2cb01341b323c3d4
>>>>
>>>> As for what would be a nice API, maybe something like one of these?
>>>>
>>>> ```js
>>>> class Lazy<T> {
>>>>     constructor(init: () => T);
>>>>     get(): T; // or error thrown
>>>> }
>>>>
>>>> function lazy<T>(init: () => T): () => T; // or error thrown
>>>>
>>>> function lazy<T>(init: () => T): {
>>>>     get(): T; // or error thrown
>>>> }
>>>> ```
>>>>
>>>> Alternatively, syntax might work, with `do` expression semantics:
>>>>
>>>> ```js
>>>> const x = lazy do { ... }
>>>> // expose via `x.get()` or just `x()`
>>>> ```
>>>>
>>>> -----
>>>>
>>>> Isiah Meadows
>>>> [hidden email]
>>>>
>>>> Looking for web consulting? Or a new website?
>>>> Send me an email and we can get started.
>>>> www.isiahmeadows.com
>>>> _______________________________________________
>>>> 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
_______________________________________________
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: Lazy evaluation

Andrea Giammarchi-2
Using weakmaps has disadvantages if instances are many, due pressure, and it's also slower than just further property access, requiring a `wm.get(...)` each time.

On top of that, you workaround the issue, but you don't solve it.

The platform way to solve your issue is the following:

```js
class Foo {
  get bar() {
    if (this.constructor.prototype === this) throw 'unauthorized';
    var result = [];
    Object.defineProperty(this, 'bar', {value: result});
    return result;
  }
}
```

I've used the long check instead of `this === Foo.prototype` because the condition `this.constructor.prototype` works through inheritance chain too.

I also use a variable and return it instead because not everyone knows that IE might go in recursion accessing a getter within the getter ;-)

Regards




On Mon, Sep 11, 2017 at 7:06 AM, Michał Wadas <[hidden email]> wrote:
BTW consider this:

```js
class Foo {
  get bar() {
    Object.defineProperty(this, 'bar', {value: []});
    return this.bar;
  }
}
const a = new Foo;
Foo.prototype.bar;
const b = new Foo;
const c = new Foo;
// b.bar === c.bar
```

So I recommend this instead:

```js
const wm = new WeakMap;
class Foo {
  get bar() {
    if (!wm.has(this)) {
      wm.set(this, []);
    }
    return wm.get(this);
  }
}
const a = new Foo;
Foo.prototype.bar;
const b = new Foo;
const c = new Foo;
// b.bar !== c.bar
```


On Fri, Sep 1, 2017 at 12:11 PM, <[hidden email]> wrote:


On August 31, 2017 6:15:20 PM GMT+02:00, Isiah Meadows <[hidden email]> wrote:
>Promises are inherently eager, but also async - consider that `new
>Promise(resolve => resolve(1))` is roughly equivalent to `var promise
>= Promise.resolve(1)`.
>
>My proposal is for a single immediate value, but created on demand
>(when you call `.get()`) rather than immediately.

But then, it seems to me Andrea's self-rewriting getter gets to the point.

Maybe there can be 'caching' getter planned as in:

Object.defineProperty(foo, "bar", {
  get: () => "baz",
  caching: true
});

with a shortcut

Object.defineLazyValue(
  foo, "bar", () => "baz");

which is implementable via a lib.

Herby
>-----
>
>Isiah Meadows
>[hidden email]
>
>Looking for web consulting? Or a new website?
>Send me an email and we can get started.
>www.isiahmeadows.com
>
>
>On Thu, Aug 31, 2017 at 10:37 AM, Naveen Chawla <[hidden email]>
>wrote:
>> Could you not do this with a promise? If not, what's missing in
>promise that
>> you could do with "lazy"? Sorry if I've missed the whole premise
>>
>> On Thu, 31 Aug 2017 at 19:09 Andrea Giammarchi
><[hidden email]>
>> wrote:
>>>
>>> the following is how I usually consider lazy values
>>>
>>> ```js
>>> class Any {
>>>   _lazy(name) {
>>>     switch (name) {
>>>       case 'uid': return Math.random();
>>>       // others ... eventually
>>>     }
>>>   }
>>>   get uid() {
>>>     var value = this._lazy('uid');
>>>     // from now on, direct access
>>>     Object.defineProperty(this, 'uid', {value});
>>>     return value;
>>>   }
>>> }
>>>
>>> const a = new Any;
>>> a.uid === a.uid; // true
>>> ```
>>>
>>> If I understand correctly your proposal is to use Lazy as generic
>>> descriptor, is that correct ?
>>>
>>> ```js
>>> Object.defineProperty({}, 'something', new Lazy(function (val) {
>>>   return this.shakaLaka ? val : 'no shakaLaka';
>>> }));
>>> ```
>>>
>>> ???
>>>
>>> If that's the case I see already people confused by arrow function
>>> in case they need to access the context,
>>> plus no property access optimization once resolved.
>>>
>>> It's also not clear if such property can be set again later on
>(right now
>>> it cannot)
>>> 'cause lazy definition doesn't always necessarily mean inability to
>>> reassign.
>>>
>>> What am I missing/misunderstanding?
>>>
>>> Regards
>>>
>>>
>>>
>>> On Thu, Aug 31, 2017 at 2:21 PM, Isiah Meadows
><[hidden email]>
>>> wrote:
>>>>
>>>> It'd be really nice if lazy values made it into the spec somehow.
>I've
>>>> already found myself using things like this [1] quite a bit, and
>I've
>>>> also found myself frequently initializing properties not on first
>>>> access.
>>>>
>>>> [1]:
>>>>
>https://gist.github.com/isiahmeadows/4c0723bdfa555a1c2cb01341b323c3d4
>>>>
>>>> As for what would be a nice API, maybe something like one of these?
>>>>
>>>> ```js
>>>> class Lazy<T> {
>>>>     constructor(init: () => T);
>>>>     get(): T; // or error thrown
>>>> }
>>>>
>>>> function lazy<T>(init: () => T): () => T; // or error thrown
>>>>
>>>> function lazy<T>(init: () => T): {
>>>>     get(): T; // or error thrown
>>>> }
>>>> ```
>>>>
>>>> Alternatively, syntax might work, with `do` expression semantics:
>>>>
>>>> ```js
>>>> const x = lazy do { ... }
>>>> // expose via `x.get()` or just `x()`
>>>> ```
>>>>
>>>> -----
>>>>
>>>> Isiah Meadows
>>>> [hidden email]
>>>>
>>>> Looking for web consulting? Or a new website?
>>>> Send me an email and we can get started.
>>>> www.isiahmeadows.com
>>>> _______________________________________________
>>>> 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
_______________________________________________
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: Re: Lazy evaluation

Darien Valentine
In reply to this post by Isiah Meadows-2
I use the WeakMap approach, too. Recently I find myself writing classes where the class has a corresponding WeakMap holding the "shadow instances" (as opposed to having one WM per property):

    const PRIV = new WeakMap();

    class Foo {
      constructor() {
        PRIV.set(this, { bar: 0, /*...other private state init...*/ });
      }

      get bar() {
        return PRIV.get(this).bar;
      }

      set bar(val) {
        if (!Number.isInteger(val)) throw new TypeError('no!');

        PRIV.get(this).bar = val;
      }
    }

I only do this for classes that are part of some public interface, where I want finer control over what state is exposed and wish to ensure that the object cannot enter an invalid state; for internal stuff it’d probably be overkill.

The property-that-redefines-itself approach makes me uncomfortable because I don’t want property access to have observable side effects from the consumer side.

    const foo = new ClassWithThatPattern;

    Object.hasOwnProperty(foo, 'bar'); // false
    foo.bar;
    Object.hasOwnProperty(foo, 'bar'); // true

In any case ... re: lazy initialization, I would agree that decorators represent a perfect way to make this pattern declarative & expressive. I suppose the private instance properties aspect of the class properties proposal, now at stage 3, also provides a way to reduce boilerplate by a bit, but not to the same degree.

(I’d second kaizu’s opinion that the example of lazy init of a db seems kind of iffy, at least for node apps, where you kinda want to know your db is working before you even init the rest of the app, as failure almost invariably represents a terminal condition — but that said, it’s just an example, and there are certainly cases where lazy init of properties is worthwhile, e.g. when you have very large collections of many small instances and only an unknown-in-advance subset will actually need such-and-such properties calculated ultimately.)

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

Re: Re: Lazy evaluation

Andrea Giammarchi-2
Darien you managed to sneak-in another pattern that has nothing to do with laziness, it's rather an approach to simulate private variables (exposing them though).

To meaningfully compare your solution with mine you need these two classes:

```js
const WM = new WeakMap();

class CaseWM {
  get bar() {
    var shadow = WM.get(this);
    if (!shadow) {
      shadow = {bar: Math.random()};
      WM.set(this, shadow);
    }
    return shadow.bar;
  }
}

class CaseLazy {
  get bar() {
    var value = Math.random();
    Object.defineProperty(this, 'bar', {value});
    return value;
  }
}
```

You can verify the benchmark here: https://jsperf.com/lazy-property-patterns

In my Chromium the lazy property is around 4X faster and there's no GC/memory pressure due WeakMap.

It's a matter of trades-off and compromise. I don't care about `hasOwnProperty` for properties defined in the prototype, it's a misleading check anyway and I don't see any real-world side effect, or better, I cannot think of a single case I've had so far that would've been problematic.

I use the `in` operator and you should probably do the same if that's a concern, or maybe explain why/when/how that could be a concern.

Regards




On Mon, Sep 11, 2017 at 9:29 AM, Darien Valentine <[hidden email]> wrote:
I use the WeakMap approach, too. Recently I find myself writing classes where the class has a corresponding WeakMap holding the "shadow instances" (as opposed to having one WM per property):

    const PRIV = new WeakMap();

    class Foo {
      constructor() {
        PRIV.set(this, { bar: 0, /*...other private state init...*/ });
      }

      get bar() {
        return PRIV.get(this).bar;
      }

      set bar(val) {
        if (!Number.isInteger(val)) throw new TypeError('no!');

        PRIV.get(this).bar = val;
      }
    }

I only do this for classes that are part of some public interface, where I want finer control over what state is exposed and wish to ensure that the object cannot enter an invalid state; for internal stuff it’d probably be overkill.

The property-that-redefines-itself approach makes me uncomfortable because I don’t want property access to have observable side effects from the consumer side.

    const foo = new ClassWithThatPattern;

    Object.hasOwnProperty(foo, 'bar'); // false
    foo.bar;
    Object.hasOwnProperty(foo, 'bar'); // true

In any case ... re: lazy initialization, I would agree that decorators represent a perfect way to make this pattern declarative & expressive. I suppose the private instance properties aspect of the class properties proposal, now at stage 3, also provides a way to reduce boilerplate by a bit, but not to the same degree.

(I’d second kaizu’s opinion that the example of lazy init of a db seems kind of iffy, at least for node apps, where you kinda want to know your db is working before you even init the rest of the app, as failure almost invariably represents a terminal condition — but that said, it’s just an example, and there are certainly cases where lazy init of properties is worthwhile, e.g. when you have very large collections of many small instances and only an unknown-in-advance subset will actually need such-and-such properties calculated ultimately.)

_______________________________________________
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: Re: Lazy evaluation

Darien Valentine
I’m not sure what was sneaky haha ... it was in response to the prior discussion of that subject, and I think it is quite related to lazy props, example code aside, since lazy props are just a specific case for the more general pattern of properties with some form of associated state / stateful behavior.

The benchmark is great info. I agree that the observability via hasOwnProperty/getOwnPropertyDescriptor is not at all likely to create issues for any given case, but I was speaking about my reservations with library code in mind. For apps or internal classes I simply use _underscoreProps for the same need. Perhaps property definition is still faster even then, in which case I’d switch to it for those cases. But for the public API of a lib, I prefer to stick with the keep-it-unobservable rule (people have relied on stranger things).

On Mon, Sep 11, 2017 at 4:54 AM, Andrea Giammarchi <[hidden email]> wrote:
Darien you managed to sneak-in another pattern that has nothing to do with laziness, it's rather an approach to simulate private variables (exposing them though).

To meaningfully compare your solution with mine you need these two classes:

```js
const WM = new WeakMap();

class CaseWM {
  get bar() {
    var shadow = WM.get(this);
    if (!shadow) {
      shadow = {bar: Math.random()};
      WM.set(this, shadow);
    }
    return shadow.bar;
  }
}

class CaseLazy {
  get bar() {
    var value = Math.random();
    Object.defineProperty(this, 'bar', {value});
    return value;
  }
}
```

You can verify the benchmark here: https://jsperf.com/lazy-property-patterns

In my Chromium the lazy property is around 4X faster and there's no GC/memory pressure due WeakMap.

It's a matter of trades-off and compromise. I don't care about `hasOwnProperty` for properties defined in the prototype, it's a misleading check anyway and I don't see any real-world side effect, or better, I cannot think of a single case I've had so far that would've been problematic.

I use the `in` operator and you should probably do the same if that's a concern, or maybe explain why/when/how that could be a concern.

Regards




On Mon, Sep 11, 2017 at 9:29 AM, Darien Valentine <[hidden email]> wrote:
I use the WeakMap approach, too. Recently I find myself writing classes where the class has a corresponding WeakMap holding the "shadow instances" (as opposed to having one WM per property):

    const PRIV = new WeakMap();

    class Foo {
      constructor() {
        PRIV.set(this, { bar: 0, /*...other private state init...*/ });
      }

      get bar() {
        return PRIV.get(this).bar;
      }

      set bar(val) {
        if (!Number.isInteger(val)) throw new TypeError('no!');

        PRIV.get(this).bar = val;
      }
    }

I only do this for classes that are part of some public interface, where I want finer control over what state is exposed and wish to ensure that the object cannot enter an invalid state; for internal stuff it’d probably be overkill.

The property-that-redefines-itself approach makes me uncomfortable because I don’t want property access to have observable side effects from the consumer side.

    const foo = new ClassWithThatPattern;

    Object.hasOwnProperty(foo, 'bar'); // false
    foo.bar;
    Object.hasOwnProperty(foo, 'bar'); // true

In any case ... re: lazy initialization, I would agree that decorators represent a perfect way to make this pattern declarative & expressive. I suppose the private instance properties aspect of the class properties proposal, now at stage 3, also provides a way to reduce boilerplate by a bit, but not to the same degree.

(I’d second kaizu’s opinion that the example of lazy init of a db seems kind of iffy, at least for node apps, where you kinda want to know your db is working before you even init the rest of the app, as failure almost invariably represents a terminal condition — but that said, it’s just an example, and there are certainly cases where lazy init of properties is worthwhile, e.g. when you have very large collections of many small instances and only an unknown-in-advance subset will actually need such-and-such properties calculated ultimately.)

_______________________________________________
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: Re: Lazy evaluation

Andrea Giammarchi-2
For apps or internal classes I simply use _underscoreProps for the same need.

that is still not an answer to lazy property definition ;-)

I agree in most you are saying but you keep diverging from the topic of this thread: Lazy evaluation

Neither `wm.get(this)` nor `this._pseudoPrivate` are strictly solutions to the topic.

That's all I'm saying.

Regards


On Mon, Sep 11, 2017 at 10:23 AM, Darien Valentine <[hidden email]> wrote:
I’m not sure what was sneaky haha ... it was in response to the prior discussion of that subject, and I think it is quite related to lazy props, example code aside, since lazy props are just a specific case for the more general pattern of properties with some form of associated state / stateful behavior.

The benchmark is great info. I agree that the observability via hasOwnProperty/getOwnPropertyDescriptor is not at all likely to create issues for any given case, but I was speaking about my reservations with library code in mind. For apps or internal classes I simply use _underscoreProps for the same need. Perhaps property definition is still faster even then, in which case I’d switch to it for those cases. But for the public API of a lib, I prefer to stick with the keep-it-unobservable rule (people have relied on stranger things).

On Mon, Sep 11, 2017 at 4:54 AM, Andrea Giammarchi <[hidden email]> wrote:
Darien you managed to sneak-in another pattern that has nothing to do with laziness, it's rather an approach to simulate private variables (exposing them though).

To meaningfully compare your solution with mine you need these two classes:

```js
const WM = new WeakMap();

class CaseWM {
  get bar() {
    var shadow = WM.get(this);
    if (!shadow) {
      shadow = {bar: Math.random()};
      WM.set(this, shadow);
    }
    return shadow.bar;
  }
}

class CaseLazy {
  get bar() {
    var value = Math.random();
    Object.defineProperty(this, 'bar', {value});
    return value;
  }
}
```

You can verify the benchmark here: https://jsperf.com/lazy-property-patterns

In my Chromium the lazy property is around 4X faster and there's no GC/memory pressure due WeakMap.

It's a matter of trades-off and compromise. I don't care about `hasOwnProperty` for properties defined in the prototype, it's a misleading check anyway and I don't see any real-world side effect, or better, I cannot think of a single case I've had so far that would've been problematic.

I use the `in` operator and you should probably do the same if that's a concern, or maybe explain why/when/how that could be a concern.

Regards




On Mon, Sep 11, 2017 at 9:29 AM, Darien Valentine <[hidden email]> wrote:
I use the WeakMap approach, too. Recently I find myself writing classes where the class has a corresponding WeakMap holding the "shadow instances" (as opposed to having one WM per property):

    const PRIV = new WeakMap();

    class Foo {
      constructor() {
        PRIV.set(this, { bar: 0, /*...other private state init...*/ });
      }

      get bar() {
        return PRIV.get(this).bar;
      }

      set bar(val) {
        if (!Number.isInteger(val)) throw new TypeError('no!');

        PRIV.get(this).bar = val;
      }
    }

I only do this for classes that are part of some public interface, where I want finer control over what state is exposed and wish to ensure that the object cannot enter an invalid state; for internal stuff it’d probably be overkill.

The property-that-redefines-itself approach makes me uncomfortable because I don’t want property access to have observable side effects from the consumer side.

    const foo = new ClassWithThatPattern;

    Object.hasOwnProperty(foo, 'bar'); // false
    foo.bar;
    Object.hasOwnProperty(foo, 'bar'); // true

In any case ... re: lazy initialization, I would agree that decorators represent a perfect way to make this pattern declarative & expressive. I suppose the private instance properties aspect of the class properties proposal, now at stage 3, also provides a way to reduce boilerplate by a bit, but not to the same degree.

(I’d second kaizu’s opinion that the example of lazy init of a db seems kind of iffy, at least for node apps, where you kinda want to know your db is working before you even init the rest of the app, as failure almost invariably represents a terminal condition — but that said, it’s just an example, and there are certainly cases where lazy init of properties is worthwhile, e.g. when you have very large collections of many small instances and only an unknown-in-advance subset will actually need such-and-such properties calculated ultimately.)

_______________________________________________
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: Re: Lazy evaluation

Darien Valentine
Hm, I’m sorry, but I don’t follow. The accessor + shadow property pattern is a (rather common?) way to achieve lazy instantiation of properties, since it provides the necessary hook for "do this on first access". But I’m not suggesting it as a new solution in any case, I was only commenting on the subject of ways-one-can-have-stateful-access (which, it seems to me, laziness is an example use case of).

In any case, apologies if you thought my comment was unhelpful.

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

Re: Re: Lazy evaluation

peter miller
In reply to this post by Andrea Giammarchi-2
Hi Andrea,

> ```
> class CaseLazy {
>   get bar() {
>     var value = Math.random();
>     Object.defineProperty(this, 'bar', {value});
>     return value;
>   }
> }
> ```

Doesn't this count as redefining the shape of the object? Or are the  
compilers fine with it?

Peter
--
"There were drawings, and sheets of paper with writing on them, and it  
seemed that they were the sustenance of life, that here were the warlocks,  
almost the vehicles of destruction of man's life, but at the same time the  
very reason for his living." --- Maeve Gilmore/Titus Awakes.
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Re: Lazy evaluation

Andrea Giammarchi-2
Hi Peter.

Unless you have a faster way to do lazy property assignment, I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)

Regards



On Mon, Sep 11, 2017 at 11:54 AM, peter miller <[hidden email]> wrote:
Hi Andrea,

```
class CaseLazy {
  get bar() {
    var value = Math.random();
    Object.defineProperty(this, 'bar', {value});
    return value;
  }
}
```

Doesn't this count as redefining the shape of the object? Or are the compilers fine with it?

Peter
--
"There were drawings, and sheets of paper with writing on them, and it seemed that they were the sustenance of life, that here were the warlocks, almost the vehicles of destruction of man's life, but at the same time the very reason for his living." --- Maeve Gilmore/Titus Awakes.


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

Re: Re: Lazy evaluation

Matthew Robb
I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)
This ^​ is very important for everyone to get on board with. Regardless the cost should be negligible as the shape is only changing at the point of delayed init. This will cause, for example V8, to deop the object and have to build a new hidden class but only the one time. I guess it would potentially be interesting to support an own property that when undefined would delegate up the proto chain.


- Matthew Robb

On Mon, Sep 11, 2017 at 7:09 AM, Andrea Giammarchi <[hidden email]> wrote:
Hi Peter.

Unless you have a faster way to do lazy property assignment, I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)

Regards



On Mon, Sep 11, 2017 at 11:54 AM, peter miller <[hidden email]> wrote:
Hi Andrea,

```
class CaseLazy {
  get bar() {
    var value = Math.random();
    Object.defineProperty(this, 'bar', {value});
    return value;
  }
}
```

Doesn't this count as redefining the shape of the object? Or are the compilers fine with it?

Peter
--
"There were drawings, and sheets of paper with writing on them, and it seemed that they were the sustenance of life, that here were the warlocks, almost the vehicles of destruction of man's life, but at the same time the very reason for his living." --- Maeve Gilmore/Titus Awakes.


_______________________________________________
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: Lazy evaluation

Steve Fink
On 9/11/17 5:36 AM, Matthew Robb wrote:
I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)
This ^​ is very important for everyone to get on board with. Regardless the cost should be negligible as the shape is only changing at the point of delayed init. This will cause, for example V8, to deop the object and have to build a new hidden class but only the one time. I guess it would potentially be interesting to support an own property that when undefined would delegate up the proto chain.

(I don't know, but) I would expect it to be worse than this. The shape is changing at the point of delayed init, which means that if an engine is associating the possible set of shapes with the constructor (or some other form of allocation site + mandatory initialization), then that site will produce multiple shapes. All code using such objects, if they ever see both shapes, will have to handle them both. Even worse, if you have several of these delayed init properties and you end up lazily initializing them in different orders (which seems relatively easy to do), then the internal slot offsets will vary.

You don't need to bend over backwards to make things easy for the VMs, but you don't want to be mean to them either. :-)

Not to mention that the observable property iteration order will vary.

On Mon, Sep 11, 2017 at 7:09 AM, Andrea Giammarchi <[hidden email]> wrote:
Hi Peter.

Unless you have a faster way to do lazy property assignment, I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)

Regards



On Mon, Sep 11, 2017 at 11:54 AM, peter miller <[hidden email]> wrote:
Hi Andrea,

```
class CaseLazy {
  get bar() {
    var value = Math.random();
    Object.defineProperty(this, 'bar', {value});
    return value;
  }
}
```

Doesn't this count as redefining the shape of the object? Or are the compilers fine with it?


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

Re: Lazy evaluation

Andrea Giammarchi-2
Steve it's not solved in any other way. Even if you use a WeakMap with an object, you gonna lazy attach properties to that object.

I honestly would like to see alternatives, if any, 'cause so far there is a benchmark and it proves already lazy property assignment is around 4x faster.

So, it's easy to say "it's not the best approach" but apparently hard to prove that's the case?

Looking forward to see better alternatives.


On Mon, Sep 11, 2017 at 8:15 PM, Steve Fink <[hidden email]> wrote:
On 9/11/17 5:36 AM, Matthew Robb wrote:
I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)
This ^​ is very important for everyone to get on board with. Regardless the cost should be negligible as the shape is only changing at the point of delayed init. This will cause, for example V8, to deop the object and have to build a new hidden class but only the one time. I guess it would potentially be interesting to support an own property that when undefined would delegate up the proto chain.

(I don't know, but) I would expect it to be worse than this. The shape is changing at the point of delayed init, which means that if an engine is associating the possible set of shapes with the constructor (or some other form of allocation site + mandatory initialization), then that site will produce multiple shapes. All code using such objects, if they ever see both shapes, will have to handle them both. Even worse, if you have several of these delayed init properties and you end up lazily initializing them in different orders (which seems relatively easy to do), then the internal slot offsets will vary.

You don't need to bend over backwards to make things easy for the VMs, but you don't want to be mean to them either. :-)

Not to mention that the observable property iteration order will vary.

On Mon, Sep 11, 2017 at 7:09 AM, Andrea Giammarchi <[hidden email]> wrote:
Hi Peter.

Unless you have a faster way to do lazy property assignment, I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)

Regards



On Mon, Sep 11, 2017 at 11:54 AM, peter miller <[hidden email]> wrote:
Hi Andrea,

```
class CaseLazy {
  get bar() {
    var value = Math.random();
    Object.defineProperty(this, 'bar', {value});
    return value;
  }
}
```

Doesn't this count as redefining the shape of the object? Or are the compilers fine with it?


_______________________________________________
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
123