Lazy evaluation
the following is how I usually consider lazy values
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 ?
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?
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 <isiahmeadows at gmail.com> 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 > me at isiahmeadows.com > > Looking for web consulting? Or a new website? > Send me an email and we can get started. > www.isiahmeadows.com > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170831/bd420401/attachment.html>
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
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 <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> > 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 >> me at isiahmeadows.com >> >> Looking for web consulting? Or a new website? >> Send me an email and we can get started. >> www.isiahmeadows.com >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss >> > > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170831/bb103cd5/attachment-0001.html>
I was going to read this in detail but I was too lazy to properly evaluate it
I was going to read this in detail but I was too lazy to properly evaluate it -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170831/0ad676b8/attachment.html>
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 me at isiahmeadows.com
Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com
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 me at isiahmeadows.com 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 <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> > 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 >> me at isiahmeadows.com >> >> Looking for web consulting? Or a new website? >> Send me an email and we can get started. >> www.isiahmeadows.com >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss > >
using it in a descriptor would get it passed the wrong
this
sorry, what?
var a = {};
var b = {get() { return this; }};
Object.defineProperty(a, 'self', b);
a.self === a; // true
> 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 <isiahmeadows at gmail.com> 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 > me at isiahmeadows.com > > 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 > <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> > > 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 > >> me at isiahmeadows.com > >> > >> Looking for web consulting? Or a new website? > >> Send me an email and we can get started. > >> www.isiahmeadows.com > >> _______________________________________________ > >> es-discuss mailing list > >> es-discuss at mozilla.org > >> https://mail.mozilla.org/listinfo/es-discuss > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170831/9a6e2dfe/attachment.html>
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.
Isiah Meadows me at isiahmeadows.com
Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com
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. ----- Isiah Meadows me at isiahmeadows.com 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 <naveen.chwl at gmail.com> 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 <andrea.giammarchi at gmail.com> > 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 <isiahmeadows at gmail.com> >> 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 >>> me at isiahmeadows.com >>> >>> Looking for web consulting? Or a new website? >>> Send me an email and we can get started. >>> www.isiahmeadows.com >>> _______________________________________________ >>> es-discuss mailing list >>> es-discuss at mozilla.org >>> https://mail.mozilla.org/listinfo/es-discuss >> >> >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss
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 me at isiahmeadows.com
Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com
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 me at isiahmeadows.com 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 <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> > 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 >> me at isiahmeadows.com >> >> 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 >> <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> >> > 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 >> >> me at isiahmeadows.com >> >> >> >> Looking for web consulting? Or a new website? >> >> Send me an email and we can get started. >> >> www.isiahmeadows.com >> >> _______________________________________________ >> >> es-discuss mailing list >> >> es-discuss at mozilla.org >> >> https://mail.mozilla.org/listinfo/es-discuss >> > >> > > >
right ... so ... I'm not sure I understand what this proposal would solve.
Instead of this:
obj.val || (obj.val = getValue())
you want to do this
(obj.val || (obj.val = new Lazy(getValue)).get();
Where is the "win" and why is that?
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 <isiahmeadows at gmail.com> 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 > me at isiahmeadows.com > > 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 > <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> > > 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 > >> me at isiahmeadows.com > >> > >> 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 > >> <andrea.giammarchi at gmail.com> 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 < > isiahmeadows at gmail.com> > >> > 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/4c0723bdfa555a1c2cb01341b323c3 > d4 > >> >> > >> >> 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 > >> >> me at isiahmeadows.com > >> >> > >> >> Looking for web consulting? Or a new website? > >> >> Send me an email and we can get started. > >> >> www.isiahmeadows.com > >> >> _______________________________________________ > >> >> es-discuss mailing list > >> >> es-discuss at mozilla.org > >> >> https://mail.mozilla.org/listinfo/es-discuss > >> > > >> > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170831/b64e2105/attachment.html>
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?
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?
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.)
Isiah Meadows me at isiahmeadows.com
Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com
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 me at isiahmeadows.com 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 <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> > 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 >> me at isiahmeadows.com >> >> 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 >> <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> >> > 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 >> >> me at isiahmeadows.com >> >> >> >> 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 >> >> <andrea.giammarchi at gmail.com> 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 >> >> > <isiahmeadows at gmail.com> >> >> > 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 >> >> >> me at isiahmeadows.com >> >> >> >> >> >> Looking for web consulting? Or a new website? >> >> >> Send me an email and we can get started. >> >> >> www.isiahmeadows.com >> >> >> _______________________________________________ >> >> >> es-discuss mailing list >> >> >> es-discuss at mozilla.org >> >> >> https://mail.mozilla.org/listinfo/es-discuss >> >> > >> >> > >> > >> > > >
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.
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:
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
[1] Class.lazy example WebReflection/prototypal/blob/master/Class.md#classlazycallback
> 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 <isiahmeadows at gmail.com> 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 > me at isiahmeadows.com > > 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 > <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> > > 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 > >> me at isiahmeadows.com > >> > >> 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 > >> <andrea.giammarchi at gmail.com> 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 < > isiahmeadows at gmail.com> > >> > 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 > >> >> me at isiahmeadows.com > >> >> > >> >> 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 > >> >> <andrea.giammarchi at gmail.com> 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 > >> >> > <isiahmeadows at gmail.com> > >> >> > 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 > >> >> >> me at isiahmeadows.com > >> >> >> > >> >> >> Looking for web consulting? Or a new website? > >> >> >> Send me an email and we can get started. > >> >> >> www.isiahmeadows.com > >> >> >> _______________________________________________ > >> >> >> es-discuss mailing list > >> >> >> es-discuss at mozilla.org > >> >> >> https://mail.mozilla.org/listinfo/es-discuss > >> >> > > >> >> > > >> > > >> > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170831/d633d8ca/attachment-0001.html>
this proposal doesn't compose well with classes
to expand a little, if you were proposing
class WithLazyVals {
lazy _db() { return new Promise(...); }
}
I would've taken first flight to come over and hug you.
Best
> 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 < andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> > 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-init >> ialization-and-why-is-it-useful >> ----- >> >> Isiah Meadows >> me at isiahmeadows.com >> >> 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 >> <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> >> > 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 >> >> me at isiahmeadows.com >> >> >> >> 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 >> >> <andrea.giammarchi at gmail.com> 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 < >> isiahmeadows at gmail.com> >> >> > 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 >> >> >> me at isiahmeadows.com >> >> >> >> >> >> 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 >> >> >> <andrea.giammarchi at gmail.com> 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 >> >> >> > <isiahmeadows at gmail.com> >> >> >> > 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/4c0723bdfa555a1c2cb0134 >> 1b323c3d4 >> >> >> >> >> >> >> >> 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 >> >> >> >> me at isiahmeadows.com >> >> >> >> >> >> >> >> Looking for web consulting? Or a new website? >> >> >> >> Send me an email and we can get started. >> >> >> >> www.isiahmeadows.com >> >> >> >> _______________________________________________ >> >> >> >> es-discuss mailing list >> >> >> >> es-discuss at mozilla.org >> >> >> >> https://mail.mozilla.org/listinfo/es-discuss >> >> >> > >> >> >> > >> >> > >> >> > >> > >> > >> > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170831/c000f5fa/attachment-0001.html>
What about this (using the stage 3 class fields proposal)?
declare function lazy<T>(init: () => T): () => T;
class WithLazyVals {
_db = lazy(() => new Promise(...));
}
Isiah Meadows me at isiahmeadows.com
Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com
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 me at isiahmeadows.com 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 <andrea.giammarchi at gmail.com> 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 > <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> >> 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 >>> me at isiahmeadows.com >>> >>> 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 >>> <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> >>> > 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 >>> >> me at isiahmeadows.com >>> >> >>> >> 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 >>> >> <andrea.giammarchi at gmail.com> 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 >>> >> > <isiahmeadows at gmail.com> >>> >> > 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 >>> >> >> me at isiahmeadows.com >>> >> >> >>> >> >> 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 >>> >> >> <andrea.giammarchi at gmail.com> 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 >>> >> >> > <isiahmeadows at gmail.com> >>> >> >> > 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 >>> >> >> >> me at isiahmeadows.com >>> >> >> >> >>> >> >> >> Looking for web consulting? Or a new website? >>> >> >> >> Send me an email and we can get started. >>> >> >> >> www.isiahmeadows.com >>> >> >> >> _______________________________________________ >>> >> >> >> es-discuss mailing list >>> >> >> >> es-discuss at mozilla.org >>> >> >> >> https://mail.mozilla.org/listinfo/es-discuss >>> >> >> > >>> >> >> > >>> >> > >>> >> > >>> > >>> > >> >> >
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?
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 <isiahmeadows at gmail.com> 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 > me at isiahmeadows.com > > 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 > <andrea.giammarchi at gmail.com> 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 > > <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> > >> 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 > >>> me at isiahmeadows.com > >>> > >>> 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 > >>> <andrea.giammarchi at gmail.com> 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 < > isiahmeadows at gmail.com> > >>> > 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 > >>> >> me at isiahmeadows.com > >>> >> > >>> >> 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 > >>> >> <andrea.giammarchi at gmail.com> 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 > >>> >> > <isiahmeadows at gmail.com> > >>> >> > 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 > >>> >> >> me at isiahmeadows.com > >>> >> >> > >>> >> >> 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 > >>> >> >> <andrea.giammarchi at gmail.com> 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 > >>> >> >> > <isiahmeadows at gmail.com> > >>> >> >> > 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 > >>> >> >> >> me at isiahmeadows.com > >>> >> >> >> > >>> >> >> >> Looking for web consulting? Or a new website? > >>> >> >> >> Send me an email and we can get started. > >>> >> >> >> www.isiahmeadows.com > >>> >> >> >> _______________________________________________ > >>> >> >> >> es-discuss mailing list > >>> >> >> >> es-discuss at mozilla.org > >>> >> >> >> https://mail.mozilla.org/listinfo/es-discuss > >>> >> >> > > >>> >> >> > > >>> >> > > >>> >> > > >>> > > >>> > > >> > >> > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170831/8a8fe8e4/attachment-0001.html>
Note the TS-ish declaration above it. That's the variant I was referring to (I presented about 3 different variants initially).
// The declaration I included
declare function lazy<T>(init: () => T): () => T;
On Thu, Aug 31, 2017 at 3:05 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> wrote:
What about this (using the stage 3 class fields proposal)?
declare function lazy<T>(init: () => T): () => T; class WithLazyVals { _db = lazy(() => new Promise(...)); }
Isiah Meadows me at isiahmeadows.com
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 <andrea.giammarchi at gmail.com> wrote:
this proposal doesn't compose well with classes
to expand a little, if you were proposing
class WithLazyVals { lazy _db() { return new Promise(...); } }
I would've taken first flight to come over and hug you.
Best
On Thu, Aug 31, 2017 at 6:25 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com> 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.
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:
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
1 Class.lazy example
WebReflection/prototypal/blob/master/Class.md#classlazycallback
On Thu, Aug 31, 2017 at 6:03 PM, Isiah Meadows <isiahmeadows at gmail.com> wrote:
It'd solve a problem similarly to Kotlin's
by lazy { ... }
delegate, .NET'sSystem.Lazy<T>
, Swift'slazy 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?
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?
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.)
[2]:
stackoverflow.com/questions/978759/what-is-lazy-initialization-and-why-is-it-useful
Isiah Meadows me at isiahmeadows.com
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 <andrea.giammarchi at gmail.com> wrote:
right ... so ... I'm not sure I understand what this proposal would solve.
Instead of this:
obj.val || (obj.val = getValue())
you want to do this
(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 <isiahmeadows at gmail.com> wrote:
With my proposed
Lazy
class, if you were to use an instance as a descriptor, thethis
value it'd receive would not be aLazy
instance like it'd expect.Consider it the difference between
a.self
andb.get()
in your example.b.get()
is what I'd be expecting.Isiah Meadows me at isiahmeadows.com
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 <andrea.giammarchi at gmail.com> wrote:
using it in a descriptor would get it passed the wrong
this
sorry, what?
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 <isiahmeadows at gmail.com> 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 wrongthis
.)Isiah Meadows me at isiahmeadows.com
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 <andrea.giammarchi at gmail.com> wrote:
the following is how I usually consider lazy values
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 ?
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?
On Thu, Aug 31, 2017 at 2:21 PM, Isiah Meadows <isiahmeadows at gmail.com> 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:
gist.github.com/isiahmeadows/4c0723bdfa555a1c2cb01341b323c3d4
As for what would be a nice API, maybe something like one of these?
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:
const x = lazy do { ... } // expose via `x.get()` or just `x()`
Isiah Meadows me at isiahmeadows.com
Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com
es-discuss mailing list es-discuss at mozilla.org, mail.mozilla.org/listinfo/es-discuss
Isiah Meadows me at isiahmeadows.com
Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com
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 <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> > 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 >> me at isiahmeadows.com >> >> 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 >> <andrea.giammarchi at gmail.com> 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 >> > <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> >> >> 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 >> >>> me at isiahmeadows.com >> >>> >> >>> 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 >> >>> <andrea.giammarchi at gmail.com> 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 >> >>> > <isiahmeadows at gmail.com> >> >>> > 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 >> >>> >> me at isiahmeadows.com >> >>> >> >> >>> >> 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 >> >>> >> <andrea.giammarchi at gmail.com> 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 >> >>> >> > <isiahmeadows at gmail.com> >> >>> >> > 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 >> >>> >> >> me at isiahmeadows.com >> >>> >> >> >> >>> >> >> 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 >> >>> >> >> <andrea.giammarchi at gmail.com> 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 >> >>> >> >> > <isiahmeadows at gmail.com> >> >>> >> >> > 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 >> >>> >> >> >> me at isiahmeadows.com >> >>> >> >> >> >> >>> >> >> >> Looking for web consulting? Or a new website? >> >>> >> >> >> Send me an email and we can get started. >> >>> >> >> >> www.isiahmeadows.com >> >>> >> >> >> _______________________________________________ >> >>> >> >> >> es-discuss mailing list >> >>> >> >> >> es-discuss at mozilla.org >> >>> >> >> >> https://mail.mozilla.org/listinfo/es-discuss >> >>> >> >> > >> >>> >> >> > >> >>> >> > >> >>> >> > >> >>> > >> >>> > >> >> >> >> >> > > > ----- Isiah Meadows me at isiahmeadows.com Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com
Sorry I don't speak TS, I speak ES.
Can you please tell me in JavaScript what does that do?
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 <isiahmeadows at gmail.com> 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 > <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> > > 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 > >> me at isiahmeadows.com > >> > >> 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 > >> <andrea.giammarchi at gmail.com> 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 > >> > <andrea.giammarchi at gmail.com> 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 < > isiahmeadows at gmail.com> > >> >> 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 > >> >>> me at isiahmeadows.com > >> >>> > >> >>> 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 > >> >>> <andrea.giammarchi at gmail.com> 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 > >> >>> > <isiahmeadows at gmail.com> > >> >>> > 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 > >> >>> >> me at isiahmeadows.com > >> >>> >> > >> >>> >> 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 > >> >>> >> <andrea.giammarchi at gmail.com> 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 > >> >>> >> > <isiahmeadows at gmail.com> > >> >>> >> > 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 > >> >>> >> >> me at isiahmeadows.com > >> >>> >> >> > >> >>> >> >> 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 > >> >>> >> >> <andrea.giammarchi at gmail.com> 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 > >> >>> >> >> > <isiahmeadows at gmail.com> > >> >>> >> >> > 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 > >> >>> >> >> >> me at isiahmeadows.com > >> >>> >> >> >> > >> >>> >> >> >> Looking for web consulting? Or a new website? > >> >>> >> >> >> Send me an email and we can get started. > >> >>> >> >> >> www.isiahmeadows.com > >> >>> >> >> >> _______________________________________________ > >> >>> >> >> >> es-discuss mailing list > >> >>> >> >> >> es-discuss at mozilla.org > >> >>> >> >> >> https://mail.mozilla.org/listinfo/es-discuss > >> >>> >> >> > > >> >>> >> >> > > >> >>> >> > > >> >>> >> > > >> >>> > > >> >>> > > >> >> > >> >> > >> > > > > > > > ----- > > Isiah Meadows > me at isiahmeadows.com > > Looking for web consulting? Or a new website? > Send me an email and we can get started. > www.isiahmeadows.com > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170831/8f6b2ad5/attachment-0001.html>
It takes a function, and returns a function that (if necessary) initializes the value and then gets it.
Isiah Meadows me at isiahmeadows.com
Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com
It takes a function, and returns a function that (if necessary) initializes the value and then gets it. ----- Isiah Meadows me at isiahmeadows.com 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 <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> > 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 >> <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> >> > 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 >> >> me at isiahmeadows.com >> >> >> >> 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 >> >> <andrea.giammarchi at gmail.com> 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 >> >> > <andrea.giammarchi at gmail.com> 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 >> >> >> <isiahmeadows at gmail.com> >> >> >> 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 >> >> >>> me at isiahmeadows.com >> >> >>> >> >> >>> 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 >> >> >>> <andrea.giammarchi at gmail.com> 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 >> >> >>> > <isiahmeadows at gmail.com> >> >> >>> > 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 >> >> >>> >> me at isiahmeadows.com >> >> >>> >> >> >> >>> >> 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 >> >> >>> >> <andrea.giammarchi at gmail.com> 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 >> >> >>> >> > <isiahmeadows at gmail.com> >> >> >>> >> > 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 >> >> >>> >> >> me at isiahmeadows.com >> >> >>> >> >> >> >> >>> >> >> 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 >> >> >>> >> >> <andrea.giammarchi at gmail.com> 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 >> >> >>> >> >> > <isiahmeadows at gmail.com> >> >> >>> >> >> > 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 >> >> >>> >> >> >> me at isiahmeadows.com >> >> >>> >> >> >> >> >> >>> >> >> >> Looking for web consulting? Or a new website? >> >> >>> >> >> >> Send me an email and we can get started. >> >> >>> >> >> >> www.isiahmeadows.com >> >> >>> >> >> >> _______________________________________________ >> >> >>> >> >> >> es-discuss mailing list >> >> >>> >> >> >> es-discuss at mozilla.org >> >> >>> >> >> >> https://mail.mozilla.org/listinfo/es-discuss >> >> >>> >> >> > >> >> >>> >> >> > >> >> >>> >> > >> >> >>> >> > >> >> >>> > >> >> >>> > >> >> >> >> >> >> >> >> > >> > >> > >> >> ----- >> >> Isiah Meadows >> me at isiahmeadows.com >> >> Looking for web consulting? Or a new website? >> Send me an email and we can get started. >> www.isiahmeadows.com > >
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
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 <isiahmeadows at gmail.com> wrote: > It takes a function, and returns a function that (if necessary) > initializes the value and then gets it. > ----- > > Isiah Meadows > me at isiahmeadows.com > > 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 > <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> > > 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 > >> <andrea.giammarchi at gmail.com> 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 < > isiahmeadows at gmail.com> > >> > 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 > >> >> me at isiahmeadows.com > >> >> > >> >> 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 > >> >> <andrea.giammarchi at gmail.com> 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 > >> >> > <andrea.giammarchi at gmail.com> 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 > >> >> >> <isiahmeadows at gmail.com> > >> >> >> 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 > >> >> >>> me at isiahmeadows.com > >> >> >>> > >> >> >>> 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 > >> >> >>> <andrea.giammarchi at gmail.com> 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 > >> >> >>> > <isiahmeadows at gmail.com> > >> >> >>> > 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 > >> >> >>> >> me at isiahmeadows.com > >> >> >>> >> > >> >> >>> >> 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 > >> >> >>> >> <andrea.giammarchi at gmail.com> 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 > >> >> >>> >> > <isiahmeadows at gmail.com> > >> >> >>> >> > 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 > >> >> >>> >> >> me at isiahmeadows.com > >> >> >>> >> >> > >> >> >>> >> >> 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 > >> >> >>> >> >> <andrea.giammarchi at gmail.com> 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 > >> >> >>> >> >> > <isiahmeadows at gmail.com> > >> >> >>> >> >> > 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 > >> >> >>> >> >> >> me at isiahmeadows.com > >> >> >>> >> >> >> > >> >> >>> >> >> >> Looking for web consulting? Or a new website? > >> >> >>> >> >> >> Send me an email and we can get started. > >> >> >>> >> >> >> www.isiahmeadows.com > >> >> >>> >> >> >> _______________________________________________ > >> >> >>> >> >> >> es-discuss mailing list > >> >> >>> >> >> >> es-discuss at mozilla.org > >> >> >>> >> >> >> https://mail.mozilla.org/listinfo/es-discuss > >> >> >>> >> >> > > >> >> >>> >> >> > > >> >> >>> >> > > >> >> >>> >> > > >> >> >>> > > >> >> >>> > > >> >> >> > >> >> >> > >> >> > > >> > > >> > > >> > >> ----- > >> > >> Isiah Meadows > >> me at isiahmeadows.com > >> > >> Looking for web consulting? Or a new website? > >> Send me an email and we can get started. > >> www.isiahmeadows.com > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170831/beea9653/attachment-0001.html>
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 me at isiahmeadows.com
Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com
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 me at isiahmeadows.com 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 <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> > wrote: >> >> It takes a function, and returns a function that (if necessary) >> initializes the value and then gets it. >> ----- >> >> Isiah Meadows >> me at isiahmeadows.com >> >> 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 >> <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> >> > 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 >> >> <andrea.giammarchi at gmail.com> 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 >> >> > <isiahmeadows at gmail.com> >> >> > 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 >> >> >> me at isiahmeadows.com >> >> >> >> >> >> 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 >> >> >> <andrea.giammarchi at gmail.com> 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 >> >> >> > <andrea.giammarchi at gmail.com> 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 >> >> >> >> <isiahmeadows at gmail.com> >> >> >> >> 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 >> >> >> >>> me at isiahmeadows.com >> >> >> >>> >> >> >> >>> 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 >> >> >> >>> <andrea.giammarchi at gmail.com> 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 >> >> >> >>> > <isiahmeadows at gmail.com> >> >> >> >>> > 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 >> >> >> >>> >> me at isiahmeadows.com >> >> >> >>> >> >> >> >> >>> >> 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 >> >> >> >>> >> <andrea.giammarchi at gmail.com> 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 >> >> >> >>> >> > <isiahmeadows at gmail.com> >> >> >> >>> >> > 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 >> >> >> >>> >> >> me at isiahmeadows.com >> >> >> >>> >> >> >> >> >> >>> >> >> 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 >> >> >> >>> >> >> <andrea.giammarchi at gmail.com> 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 >> >> >> >>> >> >> > <isiahmeadows at gmail.com> >> >> >> >>> >> >> > 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 >> >> >> >>> >> >> >> me at isiahmeadows.com >> >> >> >>> >> >> >> >> >> >> >>> >> >> >> Looking for web consulting? Or a new website? >> >> >> >>> >> >> >> Send me an email and we can get started. >> >> >> >>> >> >> >> www.isiahmeadows.com >> >> >> >>> >> >> >> _______________________________________________ >> >> >> >>> >> >> >> es-discuss mailing list >> >> >> >>> >> >> >> es-discuss at mozilla.org >> >> >> >>> >> >> >> https://mail.mozilla.org/listinfo/es-discuss >> >> >> >>> >> >> > >> >> >> >>> >> >> > >> >> >> >>> >> > >> >> >> >>> >> > >> >> >> >>> > >> >> >> >>> > >> >> >> >> >> >> >> >> >> >> >> > >> >> > >> >> > >> >> >> >> ----- >> >> >> >> Isiah Meadows >> >> me at isiahmeadows.com >> >> >> >> Looking for web consulting? Or a new website? >> >> Send me an email and we can get started. >> >> www.isiahmeadows.com >> > >> > > >
it's a matter of semantics.
If I see this
var later = anyWrappingName(() => Math.random());
// this is an assumption, not something obvious
later() === later()
If instead, I write this:
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?
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
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 <isiahmeadows at gmail.com> 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 > me at isiahmeadows.com > > 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 > <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> > > wrote: > >> > >> It takes a function, and returns a function that (if necessary) > >> initializes the value and then gets it. > >> ----- > >> > >> Isiah Meadows > >> me at isiahmeadows.com > >> > >> 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 > >> <andrea.giammarchi at gmail.com> 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 < > isiahmeadows at gmail.com> > >> > 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 > >> >> <andrea.giammarchi at gmail.com> 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 > >> >> > <isiahmeadows at gmail.com> > >> >> > 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 > >> >> >> me at isiahmeadows.com > >> >> >> > >> >> >> 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 > >> >> >> <andrea.giammarchi at gmail.com> 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 > >> >> >> > <andrea.giammarchi at gmail.com> 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 > >> >> >> >> <isiahmeadows at gmail.com> > >> >> >> >> 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 > >> >> >> >>> me at isiahmeadows.com > >> >> >> >>> > >> >> >> >>> 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 > >> >> >> >>> <andrea.giammarchi at gmail.com> 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 > >> >> >> >>> > <isiahmeadows at gmail.com> > >> >> >> >>> > 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 > >> >> >> >>> >> me at isiahmeadows.com > >> >> >> >>> >> > >> >> >> >>> >> 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 > >> >> >> >>> >> <andrea.giammarchi at gmail.com> 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 > >> >> >> >>> >> > <isiahmeadows at gmail.com> > >> >> >> >>> >> > 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 > >> >> >> >>> >> >> me at isiahmeadows.com > >> >> >> >>> >> >> > >> >> >> >>> >> >> 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 > >> >> >> >>> >> >> <andrea.giammarchi at gmail.com> 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 > >> >> >> >>> >> >> > <isiahmeadows at gmail.com> > >> >> >> >>> >> >> > 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 > >> >> >> >>> >> >> >> me at isiahmeadows.com > >> >> >> >>> >> >> >> > >> >> >> >>> >> >> >> Looking for web consulting? Or a new website? > >> >> >> >>> >> >> >> Send me an email and we can get started. > >> >> >> >>> >> >> >> www.isiahmeadows.com > >> >> >> >>> >> >> >> _______________________________________________ > >> >> >> >>> >> >> >> es-discuss mailing list > >> >> >> >>> >> >> >> es-discuss at mozilla.org > >> >> >> >>> >> >> >> https://mail.mozilla.org/listinfo/es-discuss > >> >> >> >>> >> >> > > >> >> >> >>> >> >> > > >> >> >> >>> >> > > >> >> >> >>> >> > > >> >> >> >>> > > >> >> >> >>> > > >> >> >> >> > >> >> >> >> > >> >> >> > > >> >> > > >> >> > > >> >> > >> >> ----- > >> >> > >> >> Isiah Meadows > >> >> me at isiahmeadows.com > >> >> > >> >> Looking for web consulting? Or a new website? > >> >> Send me an email and we can get started. > >> >> www.isiahmeadows.com > >> > > >> > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170831/6b6c20a9/attachment-0001.html>
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" <andrea.giammarchi at gmail.com>
wrote:
it's a matter of semantics.
If I see this
var later = anyWrappingName(() => Math.random());
// this is an assumption, not something obvious
later() === later()
If instead, I write this:
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?
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
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" <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> 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 > me at isiahmeadows.com > > 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 > <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> > > wrote: > >> > >> It takes a function, and returns a function that (if necessary) > >> initializes the value and then gets it. > >> ----- > >> > >> Isiah Meadows > >> me at isiahmeadows.com > >> > >> 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 > >> <andrea.giammarchi at gmail.com> 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 < > isiahmeadows at gmail.com> > >> > 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 > >> >> <andrea.giammarchi at gmail.com> 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 > >> >> > <isiahmeadows at gmail.com> > >> >> > 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 > >> >> >> me at isiahmeadows.com > >> >> >> > >> >> >> 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 > >> >> >> <andrea.giammarchi at gmail.com> 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 > >> >> >> > <andrea.giammarchi at gmail.com> 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 > >> >> >> >> <isiahmeadows at gmail.com> > >> >> >> >> 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-init > ialization-and-why-is-it-useful > >> >> >> >>> ----- > >> >> >> >>> > >> >> >> >>> Isiah Meadows > >> >> >> >>> me at isiahmeadows.com > >> >> >> >>> > >> >> >> >>> 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 > >> >> >> >>> <andrea.giammarchi at gmail.com> 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 > >> >> >> >>> > <isiahmeadows at gmail.com> > >> >> >> >>> > 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 > >> >> >> >>> >> me at isiahmeadows.com > >> >> >> >>> >> > >> >> >> >>> >> 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 > >> >> >> >>> >> <andrea.giammarchi at gmail.com> 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 > >> >> >> >>> >> > <isiahmeadows at gmail.com> > >> >> >> >>> >> > 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 > >> >> >> >>> >> >> me at isiahmeadows.com > >> >> >> >>> >> >> > >> >> >> >>> >> >> 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 > >> >> >> >>> >> >> <andrea.giammarchi at gmail.com> 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 > >> >> >> >>> >> >> > <isiahmeadows at gmail.com> > >> >> >> >>> >> >> > 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 > >> >> >> >>> >> >> >> me at isiahmeadows.com > >> >> >> >>> >> >> >> > >> >> >> >>> >> >> >> Looking for web consulting? Or a new website? > >> >> >> >>> >> >> >> Send me an email and we can get started. > >> >> >> >>> >> >> >> www.isiahmeadows.com > >> >> >> >>> >> >> >> _______________________________________________ > >> >> >> >>> >> >> >> es-discuss mailing list > >> >> >> >>> >> >> >> es-discuss at mozilla.org > >> >> >> >>> >> >> >> https://mail.mozilla.org/listinfo/es-discuss > >> >> >> >>> >> >> > > >> >> >> >>> >> >> > > >> >> >> >>> >> > > >> >> >> >>> >> > > >> >> >> >>> > > >> >> >> >>> > > >> >> >> >> > >> >> >> >> > >> >> >> > > >> >> > > >> >> > > >> >> > >> >> ----- > >> >> > >> >> Isiah Meadows > >> >> me at isiahmeadows.com > >> >> > >> >> Looking for web consulting? Or a new website? > >> >> Send me an email and we can get started. > >> >> www.isiahmeadows.com > >> > > >> > > > > > > _______________________________________________ es-discuss mailing list es-discuss at mozilla.org https://mail.mozilla.org/listinfo/es-discuss -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170831/33b30c2c/attachment-0001.html>
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".
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
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 <michalwadas at gmail.com> 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" <andrea.giammarchi at gmail.com> > 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 <isiahmeadows at gmail.com> > 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 >> me at isiahmeadows.com >> >> 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 >> <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> >> > wrote: >> >> >> >> It takes a function, and returns a function that (if necessary) >> >> initializes the value and then gets it. >> >> ----- >> >> >> >> Isiah Meadows >> >> me at isiahmeadows.com >> >> >> >> 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 >> >> <andrea.giammarchi at gmail.com> 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 < >> isiahmeadows at gmail.com> >> >> > 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 >> >> >> <andrea.giammarchi at gmail.com> 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 >> >> >> > <isiahmeadows at gmail.com> >> >> >> > 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 >> >> >> >> me at isiahmeadows.com >> >> >> >> >> >> >> >> 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 >> >> >> >> <andrea.giammarchi at gmail.com> 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 >> >> >> >> > <andrea.giammarchi at gmail.com> 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 >> >> >> >> >> <isiahmeadows at gmail.com> >> >> >> >> >> 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-init >> ialization-and-why-is-it-useful >> >> >> >> >>> ----- >> >> >> >> >>> >> >> >> >> >>> Isiah Meadows >> >> >> >> >>> me at isiahmeadows.com >> >> >> >> >>> >> >> >> >> >>> 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 >> >> >> >> >>> <andrea.giammarchi at gmail.com> 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 >> >> >> >> >>> > <isiahmeadows at gmail.com> >> >> >> >> >>> > 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 >> >> >> >> >>> >> me at isiahmeadows.com >> >> >> >> >>> >> >> >> >> >> >>> >> 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 >> >> >> >> >>> >> <andrea.giammarchi at gmail.com> 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 >> >> >> >> >>> >> > <isiahmeadows at gmail.com> >> >> >> >> >>> >> > 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 >> >> >> >> >>> >> >> me at isiahmeadows.com >> >> >> >> >>> >> >> >> >> >> >> >>> >> >> 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 >> >> >> >> >>> >> >> <andrea.giammarchi at gmail.com> 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 >> >> >> >> >>> >> >> > <isiahmeadows at gmail.com> >> >> >> >> >>> >> >> > 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 >> >> >> >> >>> >> >> >> me at isiahmeadows.com >> >> >> >> >>> >> >> >> >> >> >> >> >>> >> >> >> Looking for web consulting? Or a new website? >> >> >> >> >>> >> >> >> Send me an email and we can get started. >> >> >> >> >>> >> >> >> www.isiahmeadows.com >> >> >> >> >>> >> >> >> _______________________________________________ >> >> >> >> >>> >> >> >> es-discuss mailing list >> >> >> >> >>> >> >> >> es-discuss at mozilla.org >> >> >> >> >>> >> >> >> https://mail.mozilla.org/listinfo/es-discuss >> >> >> >> >>> >> >> > >> >> >> >> >>> >> >> > >> >> >> >> >>> >> > >> >> >> >> >>> >> > >> >> >> >> >>> > >> >> >> >> >>> > >> >> >> >> >> >> >> >> >> >> >> >> >> >> > >> >> >> > >> >> >> > >> >> >> >> >> >> ----- >> >> >> >> >> >> Isiah Meadows >> >> >> me at isiahmeadows.com >> >> >> >> >> >> Looking for web consulting? Or a new website? >> >> >> Send me an email and we can get started. >> >> >> www.isiahmeadows.com >> >> > >> >> > >> > >> > >> > > > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > > > > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170831/6d97b4f8/attachment-0001.html>
inline
On Sep 1, 2017, at 1:03 AM, Isiah Meadows <isiahmeadows at gmail.com> wrote:
It'd solve a problem similarly to Kotlin's
by lazy { ... }
delegate, .NET'sSystem.Lazy<T>
, Swift'slazy 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?
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).
kaizhu256.github.io/node-db-lite/build..master..travis-ci.org/apidoc.html#apidoc.element.db-lite.storageDefer, kaizhu256.github.io/node-db-lite/build..master..travis-ci.org/apidoc.html#apidoc.element.db-lite.storageDefer
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?
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 kaizhu256.github.io/node-utility2/build..master..travis-ci.org/apidoc.html#apidoc.element.utility2.middlewareFileServer, kaizhu256.github.io/node-utility2/build..master..travis-ci.org/apidoc.html#apidoc.element.utility2.middlewareFileServer
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
-
- if cache-hit, then call onError with cacheValue
-
- run onTask in background
-
- save onTask's result to cache
-
- 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(); }
inline > On Sep 1, 2017, at 1:03 AM, Isiah Meadows <isiahmeadows at gmail.com> 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). https://kaizhu256.github.io/node-db-lite/build..master..travis-ci.org/apidoc.html#apidoc.element.db-lite.storageDefer <https://kaizhu256.github.io/node-db-lite/build..master..travis-ci.org/apidoc.html#apidoc.element.db-lite.storageDefer> 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 https://kaizhu256.github.io/node-utility2/build..master..travis-ci.org/apidoc.html#apidoc.element.utility2.middlewareFileServer <https://kaizhu256.github.io/node-utility2/build..master..travis-ci.org/apidoc.html#apidoc.element.utility2.middlewareFileServer> 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(); } -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170901/2a8720f8/attachment-0001.html>
I thought decorators were nowhere higher than stage 0 (since ever)
I thought decorators were nowhere higher than stage 0 (since ever) On Thu, Aug 31, 2017 at 9:53 PM, Michał Wadas <michalwadas at gmail.com> 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" <andrea.giammarchi at gmail.com> > 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 <isiahmeadows at gmail.com> > 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 >> me at isiahmeadows.com >> >> 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 >> <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> >> > wrote: >> >> >> >> It takes a function, and returns a function that (if necessary) >> >> initializes the value and then gets it. >> >> ----- >> >> >> >> Isiah Meadows >> >> me at isiahmeadows.com >> >> >> >> 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 >> >> <andrea.giammarchi at gmail.com> 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 < >> isiahmeadows at gmail.com> >> >> > 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 >> >> >> <andrea.giammarchi at gmail.com> 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 >> >> >> > <isiahmeadows at gmail.com> >> >> >> > 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 >> >> >> >> me at isiahmeadows.com >> >> >> >> >> >> >> >> 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 >> >> >> >> <andrea.giammarchi at gmail.com> 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 >> >> >> >> > <andrea.giammarchi at gmail.com> 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 >> >> >> >> >> <isiahmeadows at gmail.com> >> >> >> >> >> 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-init >> ialization-and-why-is-it-useful >> >> >> >> >>> ----- >> >> >> >> >>> >> >> >> >> >>> Isiah Meadows >> >> >> >> >>> me at isiahmeadows.com >> >> >> >> >>> >> >> >> >> >>> 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 >> >> >> >> >>> <andrea.giammarchi at gmail.com> 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 >> >> >> >> >>> > <isiahmeadows at gmail.com> >> >> >> >> >>> > 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 >> >> >> >> >>> >> me at isiahmeadows.com >> >> >> >> >>> >> >> >> >> >> >>> >> 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 >> >> >> >> >>> >> <andrea.giammarchi at gmail.com> 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 >> >> >> >> >>> >> > <isiahmeadows at gmail.com> >> >> >> >> >>> >> > 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 >> >> >> >> >>> >> >> me at isiahmeadows.com >> >> >> >> >>> >> >> >> >> >> >> >>> >> >> 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 >> >> >> >> >>> >> >> <andrea.giammarchi at gmail.com> 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 >> >> >> >> >>> >> >> > <isiahmeadows at gmail.com> >> >> >> >> >>> >> >> > 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 >> >> >> >> >>> >> >> >> me at isiahmeadows.com >> >> >> >> >>> >> >> >> >> >> >> >> >>> >> >> >> Looking for web consulting? Or a new website? >> >> >> >> >>> >> >> >> Send me an email and we can get started. >> >> >> >> >>> >> >> >> www.isiahmeadows.com >> >> >> >> >>> >> >> >> _______________________________________________ >> >> >> >> >>> >> >> >> es-discuss mailing list >> >> >> >> >>> >> >> >> es-discuss at mozilla.org >> >> >> >> >>> >> >> >> https://mail.mozilla.org/listinfo/es-discuss >> >> >> >> >>> >> >> > >> >> >> >> >>> >> >> > >> >> >> >> >>> >> > >> >> >> >> >>> >> > >> >> >> >> >>> > >> >> >> >> >>> > >> >> >> >> >> >> >> >> >> >> >> >> >> >> > >> >> >> > >> >> >> > >> >> >> >> >> >> ----- >> >> >> >> >> >> Isiah Meadows >> >> >> me at isiahmeadows.com >> >> >> >> >> >> Looking for web consulting? Or a new website? >> >> >> Send me an email and we can get started. >> >> >> www.isiahmeadows.com >> >> > >> >> > >> > >> > >> > > > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170901/e8fae68f/attachment-0001.html>
Stage 2, but they move really slow.
Stage 2, but they move really slow. On 1 Sep 2017 9:15 am, "Andrea Giammarchi" <andrea.giammarchi at gmail.com> wrote: > I thought decorators were nowhere higher than stage 0 (since ever) > > On Thu, Aug 31, 2017 at 9:53 PM, Michał Wadas <michalwadas at gmail.com> > 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" <andrea.giammarchi at gmail.com> >> 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 <isiahmeadows at gmail.com> >> 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 >>> me at isiahmeadows.com >>> >>> 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 >>> <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com >>> > >>> > wrote: >>> >> >>> >> It takes a function, and returns a function that (if necessary) >>> >> initializes the value and then gets it. >>> >> ----- >>> >> >>> >> Isiah Meadows >>> >> me at isiahmeadows.com >>> >> >>> >> 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 >>> >> <andrea.giammarchi at gmail.com> 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 < >>> isiahmeadows at gmail.com> >>> >> > 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 >>> >> >> <andrea.giammarchi at gmail.com> 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 >>> >> >> > <isiahmeadows at gmail.com> >>> >> >> > 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 >>> >> >> >> me at isiahmeadows.com >>> >> >> >> >>> >> >> >> 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 >>> >> >> >> <andrea.giammarchi at gmail.com> 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 >>> >> >> >> > <andrea.giammarchi at gmail.com> 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 >>> >> >> >> >> <isiahmeadows at gmail.com> >>> >> >> >> >> 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 >>> >> >> >> >>> me at isiahmeadows.com >>> >> >> >> >>> >>> >> >> >> >>> 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 >>> >> >> >> >>> <andrea.giammarchi at gmail.com> 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 >>> >> >> >> >>> > <isiahmeadows at gmail.com> >>> >> >> >> >>> > 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 >>> >> >> >> >>> >> me at isiahmeadows.com >>> >> >> >> >>> >> >>> >> >> >> >>> >> 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 >>> >> >> >> >>> >> <andrea.giammarchi at gmail.com> 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 >>> >> >> >> >>> >> > <isiahmeadows at gmail.com> >>> >> >> >> >>> >> > 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 >>> >> >> >> >>> >> >> me at isiahmeadows.com >>> >> >> >> >>> >> >> >>> >> >> >> >>> >> >> 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 >>> >> >> >> >>> >> >> <andrea.giammarchi at gmail.com> 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 >>> >> >> >> >>> >> >> > <isiahmeadows at gmail.com> >>> >> >> >> >>> >> >> > 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 >>> >> >> >> >>> >> >> >> me at isiahmeadows.com >>> >> >> >> >>> >> >> >> >>> >> >> >> >>> >> >> >> Looking for web consulting? Or a new website? >>> >> >> >> >>> >> >> >> Send me an email and we can get started. >>> >> >> >> >>> >> >> >> www.isiahmeadows.com >>> >> >> >> >>> >> >> >> _______________________________________________ >>> >> >> >> >>> >> >> >> es-discuss mailing list >>> >> >> >> >>> >> >> >> es-discuss at mozilla.org >>> >> >> >> >>> >> >> >> https://mail.mozilla.org/listinfo/es-discuss >>> >> >> >> >>> >> >> > >>> >> >> >> >>> >> >> > >>> >> >> >> >>> >> > >>> >> >> >> >>> >> > >>> >> >> >> >>> > >>> >> >> >> >>> > >>> >> >> >> >> >>> >> >> >> >> >>> >> >> >> > >>> >> >> > >>> >> >> > >>> >> >> >>> >> >> ----- >>> >> >> >>> >> >> Isiah Meadows >>> >> >> me at isiahmeadows.com >>> >> >> >>> >> >> Looking for web consulting? Or a new website? >>> >> >> Send me an email and we can get started. >>> >> >> www.isiahmeadows.com >>> >> > >>> >> > >>> > >>> > >>> >> >> >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss >> >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170901/04c603bf/attachment-0001.html>
Just so that there is no confusion: There's also function expression decorators and method parameter decorators, both of which stage 0.
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" <andrea.giammarchi at gmail.com> > > wrote: > > I thought decorators were nowhere higher than stage 0 (since ever) > > > > On Thu, Aug 31, 2017 at 9:53 PM, Michał Wadas <michalwadas at gmail.com> > > > > 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" > >> <andrea.giammarchi at gmail.com> > >> 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 <isiahmeadows at gmail.com> > >> > >> 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 > >>> me at isiahmeadows.com > >>> > >>> 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 > >>> > >>> <andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com > >>> > > >>> > wrote: > >>> >> It takes a function, and returns a function that (if necessary) > >>> >> initializes the value and then gets it. > >>> >> ----- > >>> >> > >>> >> Isiah Meadows > >>> >> me at isiahmeadows.com > >>> >> > >>> >> 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 > >>> >> > >>> >> <andrea.giammarchi at gmail.com> 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 < > >>> > >>> isiahmeadows at gmail.com> > >>> > >>> >> > 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 > >>> >> >> > >>> >> >> <andrea.giammarchi at gmail.com> 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 > >>> >> >> > <isiahmeadows at gmail.com> > >>> >> >> > > >>> >> >> > 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 > >>> >> >> >> me at isiahmeadows.com > >>> >> >> >> > >>> >> >> >> 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 > >>> >> >> >> > >>> >> >> >> <andrea.giammarchi at gmail.com> 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 > >>> >> >> >> > > >>> >> >> >> > <andrea.giammarchi at gmail.com> 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 > >>> >> >> >> >> <isiahmeadows at gmail.com> > >>> >> >> >> >> > >>> >> >> >> >> 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 > >>> >> >> >> >>> me at isiahmeadows.com > >>> >> >> >> >>> > >>> >> >> >> >>> 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 > >>> >> >> >> >>> > >>> >> >> >> >>> <andrea.giammarchi at gmail.com> 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 > >>> >> >> >> >>> > <isiahmeadows at gmail.com> > >>> >> >> >> >>> > > >>> >> >> >> >>> > 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 > >>> >> >> >> >>> >> me at isiahmeadows.com > >>> >> >> >> >>> >> > >>> >> >> >> >>> >> 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 > >>> >> >> >> >>> >> > >>> >> >> >> >>> >> <andrea.giammarchi at gmail.com> 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 > >>> >> >> >> >>> >> > <isiahmeadows at gmail.com> > >>> >> >> >> >>> >> > > >>> >> >> >> >>> >> > 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 > >>> >> >> >> >>> >> >> me at isiahmeadows.com > >>> >> >> >> >>> >> >> > >>> >> >> >> >>> >> >> 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 > >>> >> >> >> >>> >> >> > >>> >> >> >> >>> >> >> <andrea.giammarchi at gmail.com> 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 > >>> >> >> >> >>> >> >> > <isiahmeadows at gmail.com> > >>> >> >> >> >>> >> >> > > >>> >> >> >> >>> >> >> > 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 > >>> >> >> >> >>> >> >> >> me at isiahmeadows.com > >>> >> >> >> >>> >> >> >> > >>> >> >> >> >>> >> >> >> Looking for web consulting? Or a new website? > >>> >> >> >> >>> >> >> >> Send me an email and we can get started. > >>> >> >> >> >>> >> >> >> www.isiahmeadows.com > >>> >> >> >> >>> >> >> >> _______________________________________________ > >>> >> >> >> >>> >> >> >> es-discuss mailing list > >>> >> >> >> >>> >> >> >> es-discuss at mozilla.org > >>> >> >> >> >>> >> >> >> https://mail.mozilla.org/listinfo/es-discuss > >>> >> >> > >>> >> >> ----- > >>> >> >> > >>> >> >> Isiah Meadows > >>> >> >> me at isiahmeadows.com > >>> >> >> > >>> >> >> Looking for web consulting? Or a new website? > >>> >> >> Send me an email and we can get started. > >>> >> >> www.isiahmeadows.com > >> > >> _______________________________________________ > >> es-discuss mailing list > >> es-discuss at mozilla.org > >> https://mail.mozilla.org/listinfo/es-discuss -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 833 bytes Desc: This is a digitally signed message part. URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170901/0b608175/attachment-0001.sig>
then yes, decorators solve my use case pretty well.
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
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 <michalwadas at gmail.com> wrote: > Stage 2, but they move really slow. > > On 1 Sep 2017 9:15 am, "Andrea Giammarchi" <andrea.giammarchi at gmail.com> > wrote: > >> I thought decorators were nowhere higher than stage 0 (since ever) >> >> On Thu, Aug 31, 2017 at 9:53 PM, Michał Wadas <michalwadas at gmail.com> >> 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" < >>> andrea.giammarchi at gmail.com> 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 <isiahmeadows at gmail.com> >>> 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 >>>> me at isiahmeadows.com >>>> >>>> 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 >>>> <andrea.giammarchi at gmail.com> 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 < >>>> isiahmeadows at gmail.com> >>>> > wrote: >>>> >> >>>> >> It takes a function, and returns a function that (if necessary) >>>> >> initializes the value and then gets it. >>>> >> ----- >>>> >> >>>> >> Isiah Meadows >>>> >> me at isiahmeadows.com >>>> >> >>>> >> 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 >>>> >> <andrea.giammarchi at gmail.com> 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 < >>>> isiahmeadows at gmail.com> >>>> >> > 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 >>>> >> >> <andrea.giammarchi at gmail.com> 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 >>>> >> >> > <isiahmeadows at gmail.com> >>>> >> >> > 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 >>>> >> >> >> me at isiahmeadows.com >>>> >> >> >> >>>> >> >> >> 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 >>>> >> >> >> <andrea.giammarchi at gmail.com> 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 >>>> >> >> >> > <andrea.giammarchi at gmail.com> 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/WebReflecti >>>> on/prototypal/blob/master/Class.md#classlazycallback >>>> >> >> >> >> >>>> >> >> >> >> >>>> >> >> >> >> >>>> >> >> >> >> On Thu, Aug 31, 2017 at 6:03 PM, Isiah Meadows >>>> >> >> >> >> <isiahmeadows at gmail.com> >>>> >> >> >> >> 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 >>>> >> >> >> >>> me at isiahmeadows.com >>>> >> >> >> >>> >>>> >> >> >> >>> 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 >>>> >> >> >> >>> <andrea.giammarchi at gmail.com> 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 >>>> >> >> >> >>> > <isiahmeadows at gmail.com> >>>> >> >> >> >>> > 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 >>>> >> >> >> >>> >> me at isiahmeadows.com >>>> >> >> >> >>> >> >>>> >> >> >> >>> >> 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 >>>> >> >> >> >>> >> <andrea.giammarchi at gmail.com> 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 >>>> >> >> >> >>> >> > <isiahmeadows at gmail.com> >>>> >> >> >> >>> >> > 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 >>>> >> >> >> >>> >> >> me at isiahmeadows.com >>>> >> >> >> >>> >> >> >>>> >> >> >> >>> >> >> 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 >>>> >> >> >> >>> >> >> <andrea.giammarchi at gmail.com> 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 >>>> >> >> >> >>> >> >> > <isiahmeadows at gmail.com> >>>> >> >> >> >>> >> >> > 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 >>>> >> >> >> >>> >> >> >> me at isiahmeadows.com >>>> >> >> >> >>> >> >> >> >>>> >> >> >> >>> >> >> >> Looking for web consulting? Or a new website? >>>> >> >> >> >>> >> >> >> Send me an email and we can get started. >>>> >> >> >> >>> >> >> >> www.isiahmeadows.com >>>> >> >> >> >>> >> >> >> _______________________________________________ >>>> >> >> >> >>> >> >> >> es-discuss mailing list >>>> >> >> >> >>> >> >> >> es-discuss at mozilla.org >>>> >> >> >> >>> >> >> >> https://mail.mozilla.org/listinfo/es-discuss >>>> >> >> >> >>> >> >> > >>>> >> >> >> >>> >> >> > >>>> >> >> >> >>> >> > >>>> >> >> >> >>> >> > >>>> >> >> >> >>> > >>>> >> >> >> >>> > >>>> >> >> >> >> >>>> >> >> >> >> >>>> >> >> >> > >>>> >> >> > >>>> >> >> > >>>> >> >> >>>> >> >> ----- >>>> >> >> >>>> >> >> Isiah Meadows >>>> >> >> me at isiahmeadows.com >>>> >> >> >>>> >> >> Looking for web consulting? Or a new website? >>>> >> >> Send me an email and we can get started. >>>> >> >> www.isiahmeadows.com >>>> >> > >>>> >> > >>>> > >>>> > >>>> >>> >>> >>> _______________________________________________ >>> es-discuss mailing list >>> es-discuss at mozilla.org >>> https://mail.mozilla.org/listinfo/es-discuss >>> >>> >>> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170901/a749d89d/attachment-0001.html>
On August 31, 2017 6:15:20 PM GMT+02:00, Isiah Meadows <isiahmeadows at gmail.com> wrote:
Promises are inherently eager, but also async - consider that
new Promise(resolve => resolve(1))
is roughly equivalent tovar 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.
On August 31, 2017 6:15:20 PM GMT+02:00, Isiah Meadows <isiahmeadows at gmail.com> 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 >me at isiahmeadows.com > >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 <naveen.chwl at gmail.com> >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 ><andrea.giammarchi at gmail.com> >> 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 ><isiahmeadows at gmail.com> >>> 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 >>>> me at isiahmeadows.com >>>> >>>> Looking for web consulting? Or a new website? >>>> Send me an email and we can get started. >>>> www.isiahmeadows.com >>>> _______________________________________________ >>>> es-discuss mailing list >>>> es-discuss at mozilla.org >>>> https://mail.mozilla.org/listinfo/es-discuss >>> >>> >>> _______________________________________________ >>> es-discuss mailing list >>> es-discuss at mozilla.org >>> https://mail.mozilla.org/listinfo/es-discuss >_______________________________________________ >es-discuss mailing list >es-discuss at mozilla.org >https://mail.mozilla.org/listinfo/es-discuss
BTW consider this:
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:
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
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, <herby at mailbox.sk> wrote: > > > On August 31, 2017 6:15:20 PM GMT+02:00, Isiah Meadows < > isiahmeadows at gmail.com> 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 > >me at isiahmeadows.com > > > >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 <naveen.chwl at gmail.com> > >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 > ><andrea.giammarchi at gmail.com> > >> 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 > ><isiahmeadows at gmail.com> > >>> 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 > >>>> me at isiahmeadows.com > >>>> > >>>> Looking for web consulting? Or a new website? > >>>> Send me an email and we can get started. > >>>> www.isiahmeadows.com > >>>> _______________________________________________ > >>>> es-discuss mailing list > >>>> es-discuss at mozilla.org > >>>> https://mail.mozilla.org/listinfo/es-discuss > >>> > >>> > >>> _______________________________________________ > >>> es-discuss mailing list > >>> es-discuss at mozilla.org > >>> https://mail.mozilla.org/listinfo/es-discuss > >_______________________________________________ > >es-discuss mailing list > >es-discuss at mozilla.org > >https://mail.mozilla.org/listinfo/es-discuss > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170911/bc89c3f5/attachment-0001.html>
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:
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 ;-)
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 <michalwadas at gmail.com> 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, <herby at mailbox.sk> wrote: > >> >> >> On August 31, 2017 6:15:20 PM GMT+02:00, Isiah Meadows < >> isiahmeadows at gmail.com> 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 >> >me at isiahmeadows.com >> > >> >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 <naveen.chwl at gmail.com> >> >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 >> ><andrea.giammarchi at gmail.com> >> >> 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 >> ><isiahmeadows at gmail.com> >> >>> 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 >> >>>> me at isiahmeadows.com >> >>>> >> >>>> Looking for web consulting? Or a new website? >> >>>> Send me an email and we can get started. >> >>>> www.isiahmeadows.com >> >>>> _______________________________________________ >> >>>> es-discuss mailing list >> >>>> es-discuss at mozilla.org >> >>>> https://mail.mozilla.org/listinfo/es-discuss >> >>> >> >>> >> >>> _______________________________________________ >> >>> es-discuss mailing list >> >>> es-discuss at mozilla.org >> >>> https://mail.mozilla.org/listinfo/es-discuss >> >_______________________________________________ >> >es-discuss mailing list >> >es-discuss at mozilla.org >> >https://mail.mozilla.org/listinfo/es-discuss >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss >> > > > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170911/c5b4a77d/attachment.html>
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;
foo.hasOwnProperty('bar'); // false
foo.bar;
foo.hasOwnProperty('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.)
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.) -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170911/6246d84a/attachment-0001.html>
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:
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: 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.
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 <valentinium at gmail.com> 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 > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170911/74b25850/attachment.html>
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).
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 < andrea.giammarchi at gmail.com> 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 <valentinium at gmail.com> > 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 >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170911/d33e1ebd/attachment-0001.html>
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.
> 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 <valentinium at gmail.com> 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 < > andrea.giammarchi at gmail.com> 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 <valentinium at gmail.com> >> 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 >>> es-discuss at mozilla.org >>> https://mail.mozilla.org/listinfo/es-discuss >>> >>> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170911/a73d4eea/attachment.html>
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.
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. -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170911/abc5da42/attachment.html>
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
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.
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 ;-)
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 <fuchsia.groan at virgin.net> 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. > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170911/53cb9300/attachment.html>
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
> 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 < andrea.giammarchi at gmail.com> 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 <fuchsia.groan at virgin.net> > 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 > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170911/6c2c8447/attachment.html>
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 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 <andrea.giammarchi at gmail.com <mailto:andrea.giammarchi at gmail.com>> 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 > <fuchsia.groan at virgin.net <mailto:fuchsia.groan at virgin.net>> 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? > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170911/528da8df/attachment.html>
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.
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 <sphink at gmail.com> 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 < > andrea.giammarchi at gmail.com> 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 <fuchsia.groan at virgin.net> >> 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 > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170911/995865de/attachment-0001.html>
www.npmjs.com/package/decorator-memoize, www.npmjs.com/package/memoized-decorator, www.npmjs.com/package/memoize-decorator
Relevant?
https://www.npmjs.com/package/decorator-memoize https://www.npmjs.com/package/memoized-decorator https://www.npmjs.com/package/memoize-decorator Relevant? On Tue, 12 Sep 2017 at 03:18 Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote: > 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 <sphink at gmail.com> 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 < >> andrea.giammarchi at gmail.com> 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 <fuchsia.groan at virgin.net >>> > 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 >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss >> >> > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170912/3a332479/attachment.html>
My intent was only to respond to the performance analysis, specifically the implication that the only performance cost is in building the new hidden class. That is not the case; everything that touches those objects is affected as well.
Whether or not it's still the right way to accomplish what you're after, I wasn't venturing an opinion. I could probably come up with a benchmark showing that your WeakMap approach can be faster -- eg by only accessing the property once, but feeding the old and new versions of the object into code that executes many many many times (doing something that never looks at that property, but is now slightly slower because it isn't monomorphic). But I suspect that for practical usage, redefining the property is faster than a WeakMap.
If I were to look beyond for other solutions for your problem, then I'm just speculating. Can decorators populate multiple properties once the expensive work is done?
I really want to tell the VM what's going on. I guess if it knew that accessing a getter property would convert it into a value property, and that it was doing something that would access the getter, then it could know to use the outgoing shape instead of the incoming shape. If only it knew that the getter was pure... but that way lies madness.
Given that most code that would slow down would also trigger the lazy defineProperty(), it's really not going to be that much of an issue. Any access after the first will see a single shape.
meh. Just take the perf hit, with awareness that you may be triggering slight slowdowns in all users of that object. Or you might not. I doubt it'll be that big, since you'll probably just end up with an inline cache for both shapes and there won't be all that much to optimize based on knowing a single shape.
Oh, and I think I was wrong about property enumeration order. The properties already existed, so defineProperty shouldn't modify the order IIUC. (I am awful with language semantics.)
My intent was only to respond to the performance analysis, specifically the implication that the only performance cost is in building the new hidden class. That is not the case; everything that touches those objects is affected as well. Whether or not it's still the right way to accomplish what you're after, I wasn't venturing an opinion. I could probably come up with a benchmark showing that your WeakMap approach can be faster -- eg by only accessing the property once, but feeding the old and new versions of the object into code that executes many many many times (doing something that never looks at that property, but is now slightly slower because it isn't monomorphic). But I suspect that for practical usage, redefining the property *is* faster than a WeakMap. If I were to look beyond for other solutions for your problem, then I'm just speculating. Can decorators populate multiple properties once the expensive work is done? I really want to tell the VM what's going on. I guess if it knew that accessing a getter property would convert it into a value property, and that it was doing something that would access the getter, then it could know to use the outgoing shape instead of the incoming shape. If only it knew that the getter was pure... but that way lies madness. Given that most code that would slow down would also trigger the lazy defineProperty(), it's really not going to be that much of an issue. Any access after the first will see a single shape. meh. Just take the perf hit, with awareness that you may be triggering slight slowdowns in all users of that object. Or you might not. I doubt it'll be that big, since you'll probably just end up with an inline cache for both shapes and there won't be all that much to optimize based on knowing a single shape. Oh, and I think I was wrong about property enumeration order. The properties already existed, so defineProperty shouldn't modify the order IIUC. (I am awful with language semantics.) On 9/11/17 2:48 PM, Andrea Giammarchi wrote: > 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 <sphink at gmail.com > <mailto:sphink at gmail.com>> 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 > <andrea.giammarchi at gmail.com <mailto:andrea.giammarchi at gmail.com>> > 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 >> <fuchsia.groan at virgin.net <mailto:fuchsia.groan at virgin.net>> >> 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 > es-discuss at mozilla.org <mailto:es-discuss at mozilla.org> > https://mail.mozilla.org/listinfo/es-discuss > <https://mail.mozilla.org/listinfo/es-discuss> > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170912/f4685ef2/attachment.html>
I think it would be nice to be able to define an own data property on an object that while defined delegated up the prototype chain. This would allow a getter in the proto to lazily assign to the own property without triggering the property setter. This is even more nice when combined with super as you could conceivably hit the setter which itself could assign to the own property of the current instance.
Is that all too complicated? It seems perfect if only a little complicated.
I think it would be nice to be able to define an own data property on an object that while defined delegated up the prototype chain. This would allow a getter in the proto to lazily assign to the own property without triggering the property setter. This is even more nice when combined with super as you could conceivably hit the setter which itself could assign to the own property of the current instance. Is that all too complicated? It seems perfect if only a little complicated. On Sep 12, 2017 5:39 PM, "Steve Fink" <sphink at gmail.com> wrote: > My intent was only to respond to the performance analysis, specifically > the implication that the only performance cost is in building the new > hidden class. That is not the case; everything that touches those objects > is affected as well. > > Whether or not it's still the right way to accomplish what you're after, I > wasn't venturing an opinion. I could probably come up with a benchmark > showing that your WeakMap approach can be faster -- eg by only accessing > the property once, but feeding the old and new versions of the object into > code that executes many many many times (doing something that never looks > at that property, but is now slightly slower because it isn't monomorphic). > But I suspect that for practical usage, redefining the property *is* faster > than a WeakMap. > > If I were to look beyond for other solutions for your problem, then I'm > just speculating. Can decorators populate multiple properties once the > expensive work is done? > > I really want to tell the VM what's going on. I guess if it knew that > accessing a getter property would convert it into a value property, and > that it was doing something that would access the getter, then it could > know to use the outgoing shape instead of the incoming shape. If only it > knew that the getter was pure... but that way lies madness. > > Given that most code that would slow down would also trigger the lazy > defineProperty(), it's really not going to be that much of an issue. Any > access after the first will see a single shape. > > meh. Just take the perf hit, with awareness that you may be triggering > slight slowdowns in all users of that object. Or you might not. I doubt > it'll be that big, since you'll probably just end up with an inline cache > for both shapes and there won't be all that much to optimize based on > knowing a single shape. > > Oh, and I think I was wrong about property enumeration order. The > properties already existed, so defineProperty shouldn't modify the order > IIUC. (I am awful with language semantics.) > > On 9/11/17 2:48 PM, Andrea Giammarchi wrote: > > 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 <sphink at gmail.com> 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 < >> andrea.giammarchi at gmail.com> 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 <fuchsia.groan at virgin.net >>> > 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 >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss >> >> > > > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170912/c07ee2dc/attachment-0001.html>
I think to the degree that almost anyone would care, the public and private key proposal would address most of these issues. The private keys would not be affected by object shape so would have rip-roaring good performance. You'd simply need to provide getter functions in the prototype that check for undefined (or whatever) in the private variable, if not undefined return value, otherwise calculate value, set private variable, and return the value. Unless you're trying to calculate pi to a million decimal places this should be pretty much good enough for anything.
And it gives you the encapsulation you get in many other programming languages such as Java and C++. Public JS APIs would end up looking a lot like these for many applications -- a bunch of private variables and public functions including getters and setters.
Basically, most of our code already looks like this but we use closures rather than private variable to accomplish this. And yes, there is a dark side to closures, specifically the challenge in implementing closed function that don't gobble up a lot of memory. In V8 if I construct an object with say 20 function properties that's 20 JSFunction objects V8 has to create, each one being on the order of 72 bytes per function. It's not too horrible if we don't have a lot of these and they have relatively long lives but problematic for lots of instantiation of objects with lots of functions. The private key proposal would address this issue.
Alex Kodat
From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Matthew Robb Sent: Tuesday, September 12, 2017 4:49 PM To: Steve Fink <sphink at gmail.com>
Cc: es-discuss at mozilla.org >> es-discuss <es-discuss at mozilla.org>
Subject: Re: Lazy evaluation
I think it would be nice to be able to define an own data property on an object that while defined delegated up the prototype chain. This would allow a getter in the proto to lazily assign to the own property without triggering the property setter. This is even more nice when combined with super as you could conceivably hit the setter which itself could assign to the own property of the current instance.
Is that all too complicated? It seems perfect if only a little complicated.
On Sep 12, 2017 5:39 PM, "Steve Fink" <mailto:sphink at gmail.com> wrote:
My intent was only to respond to the performance analysis, specifically the implication that the only performance cost is in building the new hidden class. That is not the case; everything that touches those objects is affected as well.
Whether or not it's still the right way to accomplish what you're after, I wasn't venturing an opinion. I could probably come up with a benchmark showing that your WeakMap approach can be faster -- eg by only accessing the property once, but feeding the old and new versions of the object into code that executes many many many times (doing something that never looks at that property, but is now slightly slower because it isn't monomorphic). But I suspect that for practical usage, redefining the property is faster than a WeakMap.
If I were to look beyond for other solutions for your problem, then I'm just speculating. Can decorators populate multiple properties once the expensive work is done?
I really want to tell the VM what's going on. I guess if it knew that accessing a getter property would convert it into a value property, and that it was doing something that would access the getter, then it could know to use the outgoing shape instead of the incoming shape. If only it knew that the getter was pure... but that way lies madness.
Given that most code that would slow down would also trigger the lazy defineProperty(), it's really not going to be that much of an issue. Any access after the first will see a single shape.
meh. Just take the perf hit, with awareness that you may be triggering slight slowdowns in all users of that object. Or you might not. I doubt it'll be that big, since you'll probably just end up with an inline cache for both shapes and there won't be all that much to optimize based on knowing a single shape.
Oh, and I think I was wrong about property enumeration order. The properties already existed, so defineProperty shouldn't modify the order IIUC. (I am awful with language semantics.)
On 9/11/17 2:48 PM, Andrea Giammarchi wrote: 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 <mailto:sphink at gmail.com> 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 <mailto:andrea.giammarchi at gmail.com> 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 ;-)
On Mon, Sep 11, 2017 at 11:54 AM, peter miller <mailto:fuchsia.groan at virgin.net> 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?
I think to the degree that almost anyone would care, the public and private key proposal would address most of these issues. The private keys would not be affected by object shape so would have rip-roaring good performance. You'd simply need to provide getter functions in the prototype that check for undefined (or whatever) in the private variable, if not undefined return value, otherwise calculate value, set private variable, and return the value. Unless you're trying to calculate pi to a million decimal places this should be pretty much good enough for anything. And it gives you the encapsulation you get in many other programming languages such as Java and C++. Public JS APIs would end up looking a lot like these for many applications -- a bunch of private variables and public functions including getters and setters. Basically, most of our code already looks like this but we use closures rather than private variable to accomplish this. And yes, there is a dark side to closures, specifically the challenge in implementing closed function that don't gobble up a lot of memory. In V8 if I construct an object with say 20 function properties that's 20 JSFunction objects V8 has to create, each one being on the order of 72 bytes per function. It's not too horrible if we don't have a lot of these and they have relatively long lives but problematic for lots of instantiation of objects with lots of functions. The private key proposal would address this issue. ---- Alex Kodat From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Matthew Robb Sent: Tuesday, September 12, 2017 4:49 PM To: Steve Fink <sphink at gmail.com> Cc: es-discuss at mozilla.org >> es-discuss <es-discuss at mozilla.org> Subject: Re: Lazy evaluation I think it would be nice to be able to define an own data property on an object that while defined delegated up the prototype chain. This would allow a getter in the proto to lazily assign to the own property without triggering the property setter. This is even more nice when combined with super as you could conceivably hit the setter which itself could assign to the own property of the current instance. Is that all too complicated? It seems perfect if only a little complicated. On Sep 12, 2017 5:39 PM, "Steve Fink" <mailto:sphink at gmail.com> wrote: My intent was only to respond to the performance analysis, specifically the implication that the only performance cost is in building the new hidden class. That is not the case; everything that touches those objects is affected as well. Whether or not it's still the right way to accomplish what you're after, I wasn't venturing an opinion. I could probably come up with a benchmark showing that your WeakMap approach can be faster -- eg by only accessing the property once, but feeding the old and new versions of the object into code that executes many many many times (doing something that never looks at that property, but is now slightly slower because it isn't monomorphic). But I suspect that for practical usage, redefining the property *is* faster than a WeakMap. If I were to look beyond for other solutions for your problem, then I'm just speculating. Can decorators populate multiple properties once the expensive work is done? I really want to tell the VM what's going on. I guess if it knew that accessing a getter property would convert it into a value property, and that it was doing something that would access the getter, then it could know to use the outgoing shape instead of the incoming shape. If only it knew that the getter was pure... but that way lies madness. Given that most code that would slow down would also trigger the lazy defineProperty(), it's really not going to be that much of an issue. Any access after the first will see a single shape. meh. Just take the perf hit, with awareness that you may be triggering slight slowdowns in all users of that object. Or you might not. I doubt it'll be that big, since you'll probably just end up with an inline cache for both shapes and there won't be all that much to optimize based on knowing a single shape. Oh, and I think I was wrong about property enumeration order. The properties already existed, so defineProperty shouldn't modify the order IIUC. (I am awful with language semantics.) On 9/11/17 2:48 PM, Andrea Giammarchi wrote: 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 <mailto:sphink at gmail.com> 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 <mailto:andrea.giammarchi at gmail.com> 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 <mailto:fuchsia.groan at virgin.net> 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 mailto:es-discuss at mozilla.org https://mail.mozilla.org/listinfo/es-discuss _______________________________________________ es-discuss mailing list mailto:es-discuss at mozilla.org https://mail.mozilla.org/listinfo/es-discuss
The properties already existed, so defineProperty shouldn't modify the
order IIUC
well, nope. the property existed in the prototype, not in the object.
anyway, I guess private properties, that are a possible solution, will be transpiled through a WeakMap so that most likely anything discussed in here won't make sense and the future code would look like the following
class A {
#random;
get random() {
return this.#random ||
(this.#random = Math.random());
}
}
// transpiled
var A = function (wm) {
function A() {
wm.set(this, {random: void 0});
}
Object.defineProperties(
A.prototype,
{
random: {
configurable: true,
get: function () {
return wm.get(this).random ||
(wm.get(this).random = Math.random());
}
}
}
);
return A;
}(new WeakMap);
> The properties already existed, so defineProperty shouldn't modify the order IIUC well, nope. the property existed in the prototype, not in the object. anyway, I guess private properties, that are a possible solution, will be transpiled through a WeakMap so that most likely anything discussed in here won't make sense and the future code would look like the following ```js class A { #random; get random() { return this.#random || (this.#random = Math.random()); } } // transpiled var A = function (wm) { function A() { wm.set(this, {random: void 0}); } Object.defineProperties( A.prototype, { random: { configurable: true, get: function () { return wm.get(this).random || (wm.get(this).random = Math.random()); } } } ); return A; }(new WeakMap); ``` On Tue, Sep 12, 2017 at 10:39 PM, Steve Fink <sphink at gmail.com> wrote: > My intent was only to respond to the performance analysis, specifically > the implication that the only performance cost is in building the new > hidden class. That is not the case; everything that touches those objects > is affected as well. > > Whether or not it's still the right way to accomplish what you're after, I > wasn't venturing an opinion. I could probably come up with a benchmark > showing that your WeakMap approach can be faster -- eg by only accessing > the property once, but feeding the old and new versions of the object into > code that executes many many many times (doing something that never looks > at that property, but is now slightly slower because it isn't monomorphic). > But I suspect that for practical usage, redefining the property *is* faster > than a WeakMap. > > If I were to look beyond for other solutions for your problem, then I'm > just speculating. Can decorators populate multiple properties once the > expensive work is done? > > I really want to tell the VM what's going on. I guess if it knew that > accessing a getter property would convert it into a value property, and > that it was doing something that would access the getter, then it could > know to use the outgoing shape instead of the incoming shape. If only it > knew that the getter was pure... but that way lies madness. > > Given that most code that would slow down would also trigger the lazy > defineProperty(), it's really not going to be that much of an issue. Any > access after the first will see a single shape. > > meh. Just take the perf hit, with awareness that you may be triggering > slight slowdowns in all users of that object. Or you might not. I doubt > it'll be that big, since you'll probably just end up with an inline cache > for both shapes and there won't be all that much to optimize based on > knowing a single shape. > > Oh, and I think I was wrong about property enumeration order. The > properties already existed, so defineProperty shouldn't modify the order > IIUC. (I am awful with language semantics.) > > On 9/11/17 2:48 PM, Andrea Giammarchi wrote: > > 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 <sphink at gmail.com> 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 < >> andrea.giammarchi at gmail.com> 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 <fuchsia.groan at virgin.net >>> > 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 >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss >> >> > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170913/0b0c72aa/attachment-0001.html>
What do you mean by “will be transpiled through”? My understanding of the private property proposal is that private properties will be in fixed slots (inaccessible outside the class) in the object so there would be no WeakMap. Maybe you mean "will behave more or less as if (except more efficiently)"?
Alex Kodat
From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Andrea Giammarchi Sent: Wednesday, September 13, 2017 2:31 AM To: Steve Fink <sphink at gmail.com>
Cc: es-discuss at mozilla.org Subject: Re: Lazy evaluation
The properties already existed, so defineProperty shouldn't modify the order IIUC
well, nope. the property existed in the prototype, not in the object.
anyway, I guess private properties, that are a possible solution, will be transpiled through a WeakMap so that most likely anything discussed in here won't make sense and the future code would look like the following
class A {
#random;
get random() {
return this.#random ||
(this.#random = Math.random());
}
}
// transpiled
var A = function (wm) {
function A() {
wm.set(this, {random: void 0});
}
Object.defineProperties(
A.prototype,
{
random: {
configurable: true,
get: function () {
return wm.get(this).random ||
(wm.get(this).random = Math.random());
}
}
}
);
return A;
}(new WeakMap);
On Tue, Sep 12, 2017 at 10:39 PM, Steve Fink <mailto:sphink at gmail.com> wrote:
My intent was only to respond to the performance analysis, specifically the implication that the only performance cost is in building the new hidden class. That is not the case; everything that touches those objects is affected as well.
Whether or not it's still the right way to accomplish what you're after, I wasn't venturing an opinion. I could probably come up with a benchmark showing that your WeakMap approach can be faster -- eg by only accessing the property once, but feeding the old and new versions of the object into code that executes many many many times (doing something that never looks at that property, but is now slightly slower because it isn't monomorphic). But I suspect that for practical usage, redefining the property is faster than a WeakMap.
If I were to look beyond for other solutions for your problem, then I'm just speculating. Can decorators populate multiple properties once the expensive work is done?
I really want to tell the VM what's going on. I guess if it knew that accessing a getter property would convert it into a value property, and that it was doing something that would access the getter, then it could know to use the outgoing shape instead of the incoming shape. If only it knew that the getter was pure... but that way lies madness.
Given that most code that would slow down would also trigger the lazy defineProperty(), it's really not going to be that much of an issue. Any access after the first will see a single shape.
meh. Just take the perf hit, with awareness that you may be triggering slight slowdowns in all users of that object. Or you might not. I doubt it'll be that big, since you'll probably just end up with an inline cache for both shapes and there won't be all that much to optimize based on knowing a single shape.
Oh, and I think I was wrong about property enumeration order. The properties already existed, so defineProperty shouldn't modify the order IIUC. (I am awful with language semantics.)
On 9/11/17 2:48 PM, Andrea Giammarchi wrote: 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 <mailto:sphink at gmail.com> 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 <mailto:andrea.giammarchi at gmail.com> 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 ;-)
On Mon, Sep 11, 2017 at 11:54 AM, peter miller <mailto:fuchsia.groan at virgin.net> 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?
What do you mean by “will be transpiled through”? My understanding of the private property proposal is that private properties will be in fixed slots (inaccessible outside the class) in the object so there would be no WeakMap. Maybe you mean "will behave more or less as if (except more efficiently)"? ---- Alex Kodat From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Andrea Giammarchi Sent: Wednesday, September 13, 2017 2:31 AM To: Steve Fink <sphink at gmail.com> Cc: es-discuss at mozilla.org Subject: Re: Lazy evaluation > The properties already existed, so defineProperty shouldn't modify the order IIUC well, nope. the property existed in the prototype, not in the object. anyway, I guess private properties, that are a possible solution, will be transpiled through a WeakMap so that most likely anything discussed in here won't make sense and the future code would look like the following ```js class A { #random; get random() { return this.#random || (this.#random = Math.random()); } } // transpiled var A = function (wm) { function A() { wm.set(this, {random: void 0}); } Object.defineProperties( A.prototype, { random: { configurable: true, get: function () { return wm.get(this).random || (wm.get(this).random = Math.random()); } } } ); return A; }(new WeakMap); ``` On Tue, Sep 12, 2017 at 10:39 PM, Steve Fink <mailto:sphink at gmail.com> wrote: My intent was only to respond to the performance analysis, specifically the implication that the only performance cost is in building the new hidden class. That is not the case; everything that touches those objects is affected as well. Whether or not it's still the right way to accomplish what you're after, I wasn't venturing an opinion. I could probably come up with a benchmark showing that your WeakMap approach can be faster -- eg by only accessing the property once, but feeding the old and new versions of the object into code that executes many many many times (doing something that never looks at that property, but is now slightly slower because it isn't monomorphic). But I suspect that for practical usage, redefining the property *is* faster than a WeakMap. If I were to look beyond for other solutions for your problem, then I'm just speculating. Can decorators populate multiple properties once the expensive work is done? I really want to tell the VM what's going on. I guess if it knew that accessing a getter property would convert it into a value property, and that it was doing something that would access the getter, then it could know to use the outgoing shape instead of the incoming shape. If only it knew that the getter was pure... but that way lies madness. Given that most code that would slow down would also trigger the lazy defineProperty(), it's really not going to be that much of an issue. Any access after the first will see a single shape. meh. Just take the perf hit, with awareness that you may be triggering slight slowdowns in all users of that object. Or you might not. I doubt it'll be that big, since you'll probably just end up with an inline cache for both shapes and there won't be all that much to optimize based on knowing a single shape. Oh, and I think I was wrong about property enumeration order. The properties already existed, so defineProperty shouldn't modify the order IIUC. (I am awful with language semantics.) On 9/11/17 2:48 PM, Andrea Giammarchi wrote: 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 <mailto:sphink at gmail.com> 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 <mailto:andrea.giammarchi at gmail.com> 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 <mailto:fuchsia.groan at virgin.net> 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 mailto:es-discuss at mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Also, FWIW, since we’re talking about nanoseconds of performance, here, I think slightly better than:
return this.#random || (this.#random = Math.random());
Is
return (this.#random === undefined) ? this.#random = Math.random() : this.#random;
This is because in the former, the compiled code has to determine whether this.#random is coercible into a Boolean. At the very least, it has to do an "Is this an object and, if so, not undefined ==> true" test. From first principles, the ternary expression is going to be more efficient. It simply checks if the value at this.#random is the same as the global undefined value and, if not, returns that value. No extra tests necessary.
And at least with V8 theory == practice in this case (at least in all my tests). And yes, we're talking nanosecond here.
While this looks a bit like the null propagation operator would be applicable, it's really not.
Also FWIW, if you get tired of seeing/typing undefined, we add _ to the global object which means the above can be written as:
return (this.#random === _) ? this.#random = Math.random() : this.#random;
You might think that this would perform worse than the undefined but, in fact, for V8 it's identical -- undefined and _ are both just properties of the global object. I think this reads especially well in function calls where you want an undefined placeholder
fooey(_, "whatever");
Visually, _ just says undefined to me.
Alex Kodat
From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Andrea Giammarchi Sent: Wednesday, September 13, 2017 2:31 AM To: Steve Fink <sphink at gmail.com>
Cc: es-discuss at mozilla.org Subject: Re: Lazy evaluation
The properties already existed, so defineProperty shouldn't modify the order IIUC
well, nope. the property existed in the prototype, not in the object.
anyway, I guess private properties, that are a possible solution, will be transpiled through a WeakMap so that most likely anything discussed in here won't make sense and the future code would look like the following
class A {
#random;
get random() {
return this.#random ||
(this.#random = Math.random());
}
}
// transpiled
var A = function (wm) {
function A() {
wm.set(this, {random: void 0});
}
Object.defineProperties(
A.prototype,
{
random: {
configurable: true,
get: function () {
return wm.get(this).random ||
(wm.get(this).random = Math.random());
}
}
}
);
return A;
}(new WeakMap);
Also, FWIW, since we’re talking about nanoseconds of performance, here, I think slightly better than: return this.#random || (this.#random = Math.random()); Is return (this.#random === undefined) ? this.#random = Math.random() : this.#random; This is because in the former, the compiled code has to determine whether this.#random is coercible into a Boolean. At the very least, it has to do an "Is this an object and, if so, not undefined ==> true" test. From first principles, the ternary expression is going to be more efficient. It simply checks if the value at this.#random is the same as the global undefined value and, if not, returns that value. No extra tests necessary. And at least with V8 theory == practice in this case (at least in all my tests). And yes, we're talking nanosecond here. While this looks a bit like the null propagation operator would be applicable, it's really not. Also FWIW, if you get tired of seeing/typing undefined, we add _ to the global object which means the above can be written as: return (this.#random === _) ? this.#random = Math.random() : this.#random; You might think that this would perform worse than the undefined but, in fact, for V8 it's identical -- undefined and _ are both just properties of the global object. I think this reads especially well in function calls where you want an undefined placeholder fooey(_, "whatever"); Visually, _ just says undefined to me. ---- Alex Kodat From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Andrea Giammarchi Sent: Wednesday, September 13, 2017 2:31 AM To: Steve Fink <sphink at gmail.com> Cc: es-discuss at mozilla.org Subject: Re: Lazy evaluation > The properties already existed, so defineProperty shouldn't modify the order IIUC well, nope. the property existed in the prototype, not in the object. anyway, I guess private properties, that are a possible solution, will be transpiled through a WeakMap so that most likely anything discussed in here won't make sense and the future code would look like the following ```js class A { #random; get random() { return this.#random || (this.#random = Math.random()); } } // transpiled var A = function (wm) { function A() { wm.set(this, {random: void 0}); } Object.defineProperties( A.prototype, { random: { configurable: true, get: function () { return wm.get(this).random || (wm.get(this).random = Math.random()); } } } ); return A; }(new WeakMap); ```
Nit: _
is a very valid identifier. (Consider Underscore/Lodash)
Nit: `_` is a very valid identifier. (Consider Underscore/Lodash) On Wed, Sep 13, 2017, 09:32 Alex Kodat <alexkodat at gmail.com> wrote: > Also, FWIW, since we’re talking about nanoseconds of performance, here, I > think slightly better than: > > return this.#random || (this.#random = Math.random()); > > Is > > return (this.#random === undefined) ? this.#random = Math.random() : > this.#random; > > This is because in the former, the compiled code has to determine whether > this.#random is coercible into a Boolean. At the very least, it has to do > an "Is this an object and, if so, not undefined ==> true" test. From first > principles, the ternary expression is going to be more efficient. It simply > checks if the value at this.#random is the same as the global undefined > value and, if not, returns that value. No extra tests necessary. > > And at least with V8 theory == practice in this case (at least in all my > tests). And yes, we're talking nanosecond here. > > While this looks a bit like the null propagation operator would be > applicable, it's really not. > > Also FWIW, if you get tired of seeing/typing undefined, we add _ to the > global object which means the above can be written as: > > return (this.#random === _) ? this.#random = Math.random() : > this.#random; > > You might think that this would perform worse than the undefined but, in > fact, for V8 it's identical -- undefined and _ are both just properties of > the global object. I think this reads especially well in function calls > where you want an undefined placeholder > > fooey(_, "whatever"); > > Visually, _ just says undefined to me. > > ---- > Alex Kodat > > From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of > Andrea Giammarchi > Sent: Wednesday, September 13, 2017 2:31 AM > To: Steve Fink <sphink at gmail.com> > Cc: es-discuss at mozilla.org > Subject: Re: Lazy evaluation > > > The properties already existed, so defineProperty shouldn't modify the > order IIUC > > well, nope. the property existed in the prototype, not in the object. > > anyway, I guess private properties, that are a possible solution, will be > transpiled through a WeakMap so that most likely anything discussed in here > won't make sense and the future code would look like the following > > ```js > class A { > #random; > get random() { > return this.#random || > (this.#random = Math.random()); > } > } > > > // transpiled > var A = function (wm) { > function A() { > wm.set(this, {random: void 0}); > } > Object.defineProperties( > A.prototype, > { > random: { > configurable: true, > get: function () { > return wm.get(this).random || > (wm.get(this).random = Math.random()); > } > } > } > ); > return A; > }(new WeakMap); > ``` > > > > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170913/a904373a/attachment-0001.html>
Maybe you mean "will behave more or less as if (except more efficiently)"?
no, I meant: it will transpiled into something using private WeakMaps.
I don't have any interest in talk nanoseconds for something unrelated to the topic though.
Best
> Maybe you mean "will behave more or less as if (except more efficiently)"? no, I meant: it will transpiled into something using private WeakMaps. I don't have any interest in talk nanoseconds for something unrelated to the topic though. Best Regards On Wed, Sep 13, 2017 at 1:54 PM, Alex Kodat <alexkodat at gmail.com> wrote: > What do you mean by “will be transpiled through”? My understanding of the > private property proposal is that private properties will be in fixed slots > (inaccessible outside the class) in the object so there would be no > WeakMap. Maybe you mean "will behave more or less as if (except more > efficiently)"? > > ---- > Alex Kodat > > From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of > Andrea Giammarchi > Sent: Wednesday, September 13, 2017 2:31 AM > To: Steve Fink <sphink at gmail.com> > Cc: es-discuss at mozilla.org > Subject: Re: Lazy evaluation > > > The properties already existed, so defineProperty shouldn't modify the > order IIUC > > well, nope. the property existed in the prototype, not in the object. > > anyway, I guess private properties, that are a possible solution, will be > transpiled through a WeakMap so that most likely anything discussed in here > won't make sense and the future code would look like the following > > ```js > class A { > #random; > get random() { > return this.#random || > (this.#random = Math.random()); > } > } > > > // transpiled > var A = function (wm) { > function A() { > wm.set(this, {random: void 0}); > } > Object.defineProperties( > A.prototype, > { > random: { > configurable: true, > get: function () { > return wm.get(this).random || > (wm.get(this).random = Math.random()); > } > } > } > ); > return A; > }(new WeakMap); > ``` > > > > > > > On Tue, Sep 12, 2017 at 10:39 PM, Steve Fink <mailto:sphink at gmail.com> > wrote: > My intent was only to respond to the performance analysis, specifically > the implication that the only performance cost is in building the new > hidden class. That is not the case; everything that touches those objects > is affected as well. > > Whether or not it's still the right way to accomplish what you're after, I > wasn't venturing an opinion. I could probably come up with a benchmark > showing that your WeakMap approach can be faster -- eg by only accessing > the property once, but feeding the old and new versions of the object into > code that executes many many many times (doing something that never looks > at that property, but is now slightly slower because it isn't monomorphic). > But I suspect that for practical usage, redefining the property *is* faster > than a WeakMap. > > If I were to look beyond for other solutions for your problem, then I'm > just speculating. Can decorators populate multiple properties once the > expensive work is done? > > I really want to tell the VM what's going on. I guess if it knew that > accessing a getter property would convert it into a value property, and > that it was doing something that would access the getter, then it could > know to use the outgoing shape instead of the incoming shape. If only it > knew that the getter was pure... but that way lies madness. > > Given that most code that would slow down would also trigger the lazy > defineProperty(), it's really not going to be that much of an issue. Any > access after the first will see a single shape. > > meh. Just take the perf hit, with awareness that you may be triggering > slight slowdowns in all users of that object. Or you might not. I doubt > it'll be that big, since you'll probably just end up with an inline cache > for both shapes and there won't be all that much to optimize based on > knowing a single shape. > > Oh, and I think I was wrong about property enumeration order. The > properties already existed, so defineProperty shouldn't modify the order > IIUC. (I am awful with language semantics.) > > On 9/11/17 2:48 PM, Andrea Giammarchi wrote: > 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 <mailto:sphink at gmail.com> > 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 <mailto: > andrea.giammarchi at gmail.com> 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 <mailto: > fuchsia.groan at virgin.net> 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 > mailto:es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170913/756e5095/attachment-0001.html>
inline
class A { #random; get random() { return this.#random || (this.#random = Math.random()); } } // transpiled var A = function (wm) { function A() { wm.set(this, {random: void 0}); } Object.defineProperties( A.prototype, { random: { configurable: true, get: function () { return wm.get(this).random || (wm.get(this).random = Math.random()); } } } ); return A; }(new WeakMap);
-
overloaded getters/setters and Object.defineProperties are generally over-engineered anti-patterns in javascript-programming
- please use normal method-calls instead, which are more maintainable
-
WeakMap is an over-engineered anti-pattern in javascript-programming
- they are inferior to using a plain-object cache-dictionary in pretty much every use-case.
- WeakMap's implicit-garbage-collecting feature is error-prone, hard to validate with tests, and generally becomes unmaintainable over time.
/*jslint node: true */
'use strict';
function LazyClass() {
this.cacheDict = {};
}
LazyClass.prototype.getRandomCached = function () {
this.cacheDict.random = this.cacheDict.random || Math.random();
return this.cacheDict.random;
};
LazyClass.prototype.clearCache = function () {
this.cacheDict = {};
};
var lazyObject = new LazyClass();
console.log(lazyObject.getRandomCached());
console.log(lazyObject.getRandomCached());
lazyObject.clearCache();
console.log(lazyObject.getRandomCached());
inline > ```js > class A { > #random; > get random() { > return this.#random || > (this.#random = Math.random()); > } > } > > > // transpiled > var A = function (wm) { > function A() { > wm.set(this, {random: void 0}); > } > Object.defineProperties( > A.prototype, > { > random: { > configurable: true, > get: function () { > return wm.get(this).random || > (wm.get(this).random = Math.random()); > } > } > } > ); > return A; > }(new WeakMap); > ``` - overloaded getters/setters and Object.defineProperties are generally over-engineered anti-patterns in javascript-programming * please use normal method-calls instead, which are more maintainable - WeakMap is an over-engineered anti-pattern in javascript-programming * they are inferior to using a plain-object cache-dictionary in pretty much every use-case. * WeakMap's implicit-garbage-collecting feature is error-prone, hard to validate with tests, and generally becomes unmaintainable over time. ```javascript /*jslint node: true */ 'use strict'; function LazyClass() { this.cacheDict = {}; } LazyClass.prototype.getRandomCached = function () { this.cacheDict.random = this.cacheDict.random || Math.random(); return this.cacheDict.random; }; LazyClass.prototype.clearCache = function () { this.cacheDict = {}; }; var lazyObject = new LazyClass(); console.log(lazyObject.getRandomCached()); console.log(lazyObject.getRandomCached()); lazyObject.clearCache(); console.log(lazyObject.getRandomCached()); ``` > On Sep 13, 2017, at 9:32 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote: > > > Maybe you mean "will behave more or less as if (except more efficiently)"? > > no, I meant: it will transpiled into something using private WeakMaps. > > I don't have any interest in talk nanoseconds for something unrelated to the topic though. > > Best Regards > > On Wed, Sep 13, 2017 at 1:54 PM, Alex Kodat <alexkodat at gmail.com <mailto:alexkodat at gmail.com>> wrote: > What do you mean by “will be transpiled through”? My understanding of the private property proposal is that private properties will be in fixed slots (inaccessible outside the class) in the object so there would be no WeakMap. Maybe you mean "will behave more or less as if (except more efficiently)"? > > ---- > Alex Kodat > > From: es-discuss [mailto:es-discuss-bounces at mozilla.org <mailto:es-discuss-bounces at mozilla.org>] On Behalf Of Andrea Giammarchi > Sent: Wednesday, September 13, 2017 2:31 AM > To: Steve Fink <sphink at gmail.com <mailto:sphink at gmail.com>> > Cc: es-discuss at mozilla.org <mailto:es-discuss at mozilla.org> > Subject: Re: Lazy evaluation > > > The properties already existed, so defineProperty shouldn't modify the order IIUC > > well, nope. the property existed in the prototype, not in the object. > > anyway, I guess private properties, that are a possible solution, will be transpiled through a WeakMap so that most likely anything discussed in here won't make sense and the future code would look like the following > > ```js > class A { > #random; > get random() { > return this.#random || > (this.#random = Math.random()); > } > } > > > // transpiled > var A = function (wm) { > function A() { > wm.set(this, {random: void 0}); > } > Object.defineProperties( > A.prototype, > { > random: { > configurable: true, > get: function () { > return wm.get(this).random || > (wm.get(this).random = Math.random()); > } > } > } > ); > return A; > }(new WeakMap); > ``` > > > > > > > On Tue, Sep 12, 2017 at 10:39 PM, Steve Fink <mailto:sphink at gmail.com <mailto:sphink at gmail.com>> wrote: > My intent was only to respond to the performance analysis, specifically the implication that the only performance cost is in building the new hidden class. That is not the case; everything that touches those objects is affected as well. > > Whether or not it's still the right way to accomplish what you're after, I wasn't venturing an opinion. I could probably come up with a benchmark showing that your WeakMap approach can be faster -- eg by only accessing the property once, but feeding the old and new versions of the object into code that executes many many many times (doing something that never looks at that property, but is now slightly slower because it isn't monomorphic). But I suspect that for practical usage, redefining the property *is* faster than a WeakMap. > > If I were to look beyond for other solutions for your problem, then I'm just speculating. Can decorators populate multiple properties once the expensive work is done? > > I really want to tell the VM what's going on. I guess if it knew that accessing a getter property would convert it into a value property, and that it was doing something that would access the getter, then it could know to use the outgoing shape instead of the incoming shape. If only it knew that the getter was pure... but that way lies madness. > > Given that most code that would slow down would also trigger the lazy defineProperty(), it's really not going to be that much of an issue. Any access after the first will see a single shape. > > meh. Just take the perf hit, with awareness that you may be triggering slight slowdowns in all users of that object. Or you might not. I doubt it'll be that big, since you'll probably just end up with an inline cache for both shapes and there won't be all that much to optimize based on knowing a single shape. > > Oh, and I think I was wrong about property enumeration order. The properties already existed, so defineProperty shouldn't modify the order IIUC. (I am awful with language semantics.) > > On 9/11/17 2:48 PM, Andrea Giammarchi wrote: > 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 <mailto:sphink at gmail.com <mailto:sphink at gmail.com>> 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 <mailto:andrea.giammarchi at gmail.com <mailto:andrea.giammarchi at gmail.com>> 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 <mailto:fuchsia.groan at virgin.net <mailto:fuchsia.groan at virgin.net>> 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 > mailto:es-discuss at mozilla.org <mailto:es-discuss at mozilla.org> > https://mail.mozilla.org/listinfo/es-discuss <https://mail.mozilla.org/listinfo/es-discuss> > > > > > > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170914/4b1c1895/attachment-0001.html> -------------- next part -------------- A non-text attachment was scrubbed... Name: PastedGraphic-4.png Type: image/png Size: 243785 bytes Desc: not available URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170914/4b1c1895/attachment-0001.png>
please use normal method-calls instead, which are more maintainable
no, thanks.
WeakMap's implicit-garbage-collecting feature is error-prone, hard to
validate with tests, and generally becomes unmaintainable over time.
no, thanks.
We were discussing patterns here, nothing to "please don't" and IMO, dogma oriented rules are over-engineered anti-pattern too.
Let's not make this ML an awkward place for who wants to just learn or explore JS language features, thanks.
> please use normal method-calls instead, which are more maintainable no, thanks. > WeakMap's implicit-garbage-collecting feature is error-prone, hard to validate with tests, and generally becomes unmaintainable over time. no, thanks. We were discussing patterns here, nothing to "please don't" and IMO, dogma oriented rules are over-engineered anti-pattern too. Let's not make this ML an awkward place for who wants to just learn or explore JS language features, thanks. On Thu, Sep 14, 2017 at 9:55 AM, kai zhu <kaizhu256 at gmail.com> wrote: > inline > > ```js >> class A { >> #random; >> get random() { >> return this.#random || >> (this.#random = Math.random()); >> } >> } >> >> >> // transpiled >> var A = function (wm) { >> function A() { >> wm.set(this, {random: void 0}); >> } >> Object.defineProperties( >> A.prototype, >> { >> random: { >> configurable: true, >> get: function () { >> return wm.get(this).random || >> (wm.get(this).random = Math.random()); >> } >> } >> } >> ); >> return A; >> }(new WeakMap); >> ``` > > > - overloaded getters/setters and Object.defineProperties are generally *over-engineered > *anti-patterns in javascript-programming > * please use normal method-calls instead, which are more maintainable > > - WeakMap is an *over-engineered *anti-pattern in javascript-programming > * they are inferior to using a plain-object cache-dictionary in pretty > much every use-case. > * WeakMap's implicit-garbage-collecting feature is error-prone, hard to > validate with tests, and generally becomes unmaintainable over time. > > ```javascript > /*jslint node: true */ > 'use strict'; > > function LazyClass() { > this.cacheDict = {}; > } > LazyClass.prototype.getRandomCached = function () { > this.cacheDict.random = this.cacheDict.random || Math.random(); > return this.cacheDict.random; > }; > LazyClass.prototype.clearCache = function () { > this.cacheDict = {}; > }; > > var lazyObject = new LazyClass(); > console.log(lazyObject.getRandomCached()); > console.log(lazyObject.getRandomCached()); > lazyObject.clearCache(); > console.log(lazyObject.getRandomCached()); > ``` > > > > > On Sep 13, 2017, at 9:32 PM, Andrea Giammarchi < > andrea.giammarchi at gmail.com> wrote: > > > Maybe you mean "will behave more or less as if (except more > efficiently)"? > > no, I meant: it will transpiled into something using private WeakMaps. > > I don't have any interest in talk nanoseconds for something unrelated to > the topic though. > > Best Regards > > On Wed, Sep 13, 2017 at 1:54 PM, Alex Kodat <alexkodat at gmail.com> wrote: > >> What do you mean by “will be transpiled through”? My understanding of the >> private property proposal is that private properties will be in fixed slots >> (inaccessible outside the class) in the object so there would be no >> WeakMap. Maybe you mean "will behave more or less as if (except more >> efficiently)"? >> >> ---- >> Alex Kodat >> >> From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of >> Andrea Giammarchi >> Sent: Wednesday, September 13, 2017 2:31 AM >> To: Steve Fink <sphink at gmail.com> >> Cc: es-discuss at mozilla.org >> Subject: Re: Lazy evaluation >> >> > The properties already existed, so defineProperty shouldn't modify the >> order IIUC >> >> well, nope. the property existed in the prototype, not in the object. >> >> anyway, I guess private properties, that are a possible solution, will be >> transpiled through a WeakMap so that most likely anything discussed in here >> won't make sense and the future code would look like the following >> >> ```js >> class A { >> #random; >> get random() { >> return this.#random || >> (this.#random = Math.random()); >> } >> } >> >> >> // transpiled >> var A = function (wm) { >> function A() { >> wm.set(this, {random: void 0}); >> } >> Object.defineProperties( >> A.prototype, >> { >> random: { >> configurable: true, >> get: function () { >> return wm.get(this).random || >> (wm.get(this).random = Math.random()); >> } >> } >> } >> ); >> return A; >> }(new WeakMap); >> ``` >> >> >> >> >> >> >> On Tue, Sep 12, 2017 at 10:39 PM, Steve Fink <mailto:sphink at gmail.com> >> wrote: >> My intent was only to respond to the performance analysis, specifically >> the implication that the only performance cost is in building the new >> hidden class. That is not the case; everything that touches those objects >> is affected as well. >> >> Whether or not it's still the right way to accomplish what you're after, >> I wasn't venturing an opinion. I could probably come up with a benchmark >> showing that your WeakMap approach can be faster -- eg by only accessing >> the property once, but feeding the old and new versions of the object into >> code that executes many many many times (doing something that never looks >> at that property, but is now slightly slower because it isn't monomorphic). >> But I suspect that for practical usage, redefining the property *is* faster >> than a WeakMap. >> >> If I were to look beyond for other solutions for your problem, then I'm >> just speculating. Can decorators populate multiple properties once the >> expensive work is done? >> >> I really want to tell the VM what's going on. I guess if it knew that >> accessing a getter property would convert it into a value property, and >> that it was doing something that would access the getter, then it could >> know to use the outgoing shape instead of the incoming shape. If only it >> knew that the getter was pure... but that way lies madness. >> >> Given that most code that would slow down would also trigger the lazy >> defineProperty(), it's really not going to be that much of an issue. Any >> access after the first will see a single shape. >> >> meh. Just take the perf hit, with awareness that you may be triggering >> slight slowdowns in all users of that object. Or you might not. I doubt >> it'll be that big, since you'll probably just end up with an inline cache >> for both shapes and there won't be all that much to optimize based on >> knowing a single shape. >> >> Oh, and I think I was wrong about property enumeration order. The >> properties already existed, so defineProperty shouldn't modify the order >> IIUC. (I am awful with language semantics.) >> >> On 9/11/17 2:48 PM, Andrea Giammarchi wrote: >> 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 <mailto:sphink at gmail.com> >> 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 <mailto: >> andrea.giammarchi at gmail.com> 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 <mailto: >> fuchsia.groan at virgin.net> 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 >> mailto:es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss >> >> >> >> >> > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > > > > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170914/e70da9c4/attachment-0001.html> -------------- next part -------------- A non-text attachment was scrubbed... Name: PastedGraphic-4.png Type: image/png Size: 243785 bytes Desc: not available URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170914/e70da9c4/attachment-0001.png>
I realize in going over the last few e-mails for this thread, that as usual, I’m to blame for a bit of poor communication. I had intended to include the link to the public-private field proposal in my comments about it but whiffed. So the link: tc39.github.io/proposal-class-fields. And yes, this link has appeared on other e-mails (sorry?).
This brings me to a (noobie) meta-question. This proposal is very interesting and looks nice to me and it is indicated to be in Stage 3. This suggests that it's pretty close to going in as-is into the ECMAScript standard? In case I'm not the last person in the world to find the process: tc39.github.io/process-document.
So is this the appropriate forum for questions or comments about this spec? I notice many of the very interesting discussions leading up to this proposal happened elsewhere (mostly GitHub). But there's no link in the spec proposal to a discussion page so maybe this is it? Sorry if this is a stupid question.
Alex Kodat
I realize in going over the last few e-mails for this thread, that as usual, I’m to blame for a bit of poor communication. I had intended to include the link to the public-private field proposal in my comments about it but whiffed. So the link: https://tc39.github.io/proposal-class-fields/. And yes, this link has appeared on other e-mails (sorry?). This brings me to a (noobie) meta-question. This proposal is very interesting and looks nice to me and it is indicated to be in Stage 3. This suggests that it's pretty close to going in as-is into the ECMAScript standard? In case I'm not the last person in the world to find the process: https://tc39.github.io/process-document/. So is this the appropriate forum for questions or comments about this spec? I notice many of the very interesting discussions leading up to this proposal happened elsewhere (mostly GitHub). But there's no link in the spec proposal to a discussion page so maybe this is it? Sorry if this is a stupid question. ---- Alex Kodat
you can call it dogma. i call it the the javascript-way of solving frontend-engineering problems, which is simpler and more practical for laymen than what i learned from cs-theory in school using c++/java.
its time we showed some pride in javascript (like the haskell/lisp/etc folks), and admit some of our 20+ year-old “hacks” are actually good design-patterns that rival and challenge traditional cs-teachings, instead of replacing them with bad-practices from other languages (cough operator-overloading cough).
you can call it dogma. i call it the the javascript-way of solving frontend-engineering problems, which is simpler and more practical for laymen than what i learned from cs-theory in school using c++/java. its time we showed some pride in javascript (like the haskell/lisp/etc folks), and admit some of our 20+ year-old “hacks” are actually good design-patterns that rival and challenge traditional cs-teachings, instead of replacing them with bad-practices from other languages (*cough* operator-overloading *cough*). > On Sep 14, 2017, at 4:26 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote: > > > please use normal method-calls instead, which are more maintainable > > no, thanks. > > > > WeakMap's implicit-garbage-collecting feature is error-prone, hard to validate with tests, and generally becomes unmaintainable over time. > > no, thanks. > > We were discussing patterns here, nothing to "please don't" and IMO, dogma oriented rules are over-engineered anti-pattern too. > > Let's not make this ML an awkward place for who wants to just learn or explore JS language features, thanks. > > > > On Thu, Sep 14, 2017 at 9:55 AM, kai zhu <kaizhu256 at gmail.com <mailto:kaizhu256 at gmail.com>> wrote: > inline > >> ```js >> class A { >> #random; >> get random() { >> return this.#random || >> (this.#random = Math.random()); >> } >> } >> >> >> // transpiled >> var A = function (wm) { >> function A() { >> wm.set(this, {random: void 0}); >> } >> Object.defineProperties( >> A.prototype, >> { >> random: { >> configurable: true, >> get: function () { >> return wm.get(this).random || >> (wm.get(this).random = Math.random()); >> } >> } >> } >> ); >> return A; >> }(new WeakMap); >> ``` > > > - overloaded getters/setters and Object.defineProperties are generally over-engineered anti-patterns in javascript-programming > * please use normal method-calls instead, which are more maintainable > > - WeakMap is an over-engineered anti-pattern in javascript-programming > * they are inferior to using a plain-object cache-dictionary in pretty much every use-case. > * WeakMap's implicit-garbage-collecting feature is error-prone, hard to validate with tests, and generally becomes unmaintainable over time. > > ```javascript > /*jslint node: true */ > 'use strict'; > > function LazyClass() { > this.cacheDict = {}; > } > LazyClass.prototype.getRandomCached = function () { > this.cacheDict.random = this.cacheDict.random || Math.random(); > return this.cacheDict.random; > }; > LazyClass.prototype.clearCache = function () { > this.cacheDict = {}; > }; > > var lazyObject = new LazyClass(); > console.log(lazyObject.getRandomCached()); > console.log(lazyObject.getRandomCached()); > lazyObject.clearCache(); > console.log(lazyObject.getRandomCached()); > ``` > > <PastedGraphic-4.png> > > > >> On Sep 13, 2017, at 9:32 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com <mailto:andrea.giammarchi at gmail.com>> wrote: >> >> > Maybe you mean "will behave more or less as if (except more efficiently)"? >> >> no, I meant: it will transpiled into something using private WeakMaps. >> >> I don't have any interest in talk nanoseconds for something unrelated to the topic though. >> >> Best Regards >> >> On Wed, Sep 13, 2017 at 1:54 PM, Alex Kodat <alexkodat at gmail.com <mailto:alexkodat at gmail.com>> wrote: >> What do you mean by “will be transpiled through”? My understanding of the private property proposal is that private properties will be in fixed slots (inaccessible outside the class) in the object so there would be no WeakMap. Maybe you mean "will behave more or less as if (except more efficiently)"? >> >> ---- >> Alex Kodat >> >> From: es-discuss [mailto:es-discuss-bounces at mozilla.org <mailto:es-discuss-bounces at mozilla.org>] On Behalf Of Andrea Giammarchi >> Sent: Wednesday, September 13, 2017 2:31 AM >> To: Steve Fink <sphink at gmail.com <mailto:sphink at gmail.com>> >> Cc: es-discuss at mozilla.org <mailto:es-discuss at mozilla.org> >> Subject: Re: Lazy evaluation >> >> > The properties already existed, so defineProperty shouldn't modify the order IIUC >> >> well, nope. the property existed in the prototype, not in the object. >> >> anyway, I guess private properties, that are a possible solution, will be transpiled through a WeakMap so that most likely anything discussed in here won't make sense and the future code would look like the following >> >> ```js >> class A { >> #random; >> get random() { >> return this.#random || >> (this.#random = Math.random()); >> } >> } >> >> >> // transpiled >> var A = function (wm) { >> function A() { >> wm.set(this, {random: void 0}); >> } >> Object.defineProperties( >> A.prototype, >> { >> random: { >> configurable: true, >> get: function () { >> return wm.get(this).random || >> (wm.get(this).random = Math.random()); >> } >> } >> } >> ); >> return A; >> }(new WeakMap); >> ``` >> >> >> >> >> >> >> On Tue, Sep 12, 2017 at 10:39 PM, Steve Fink <mailto:sphink at gmail.com <mailto:sphink at gmail.com>> wrote: >> My intent was only to respond to the performance analysis, specifically the implication that the only performance cost is in building the new hidden class. That is not the case; everything that touches those objects is affected as well. >> >> Whether or not it's still the right way to accomplish what you're after, I wasn't venturing an opinion. I could probably come up with a benchmark showing that your WeakMap approach can be faster -- eg by only accessing the property once, but feeding the old and new versions of the object into code that executes many many many times (doing something that never looks at that property, but is now slightly slower because it isn't monomorphic). But I suspect that for practical usage, redefining the property *is* faster than a WeakMap. >> >> If I were to look beyond for other solutions for your problem, then I'm just speculating. Can decorators populate multiple properties once the expensive work is done? >> >> I really want to tell the VM what's going on. I guess if it knew that accessing a getter property would convert it into a value property, and that it was doing something that would access the getter, then it could know to use the outgoing shape instead of the incoming shape. If only it knew that the getter was pure... but that way lies madness. >> >> Given that most code that would slow down would also trigger the lazy defineProperty(), it's really not going to be that much of an issue. Any access after the first will see a single shape. >> >> meh. Just take the perf hit, with awareness that you may be triggering slight slowdowns in all users of that object. Or you might not. I doubt it'll be that big, since you'll probably just end up with an inline cache for both shapes and there won't be all that much to optimize based on knowing a single shape. >> >> Oh, and I think I was wrong about property enumeration order. The properties already existed, so defineProperty shouldn't modify the order IIUC. (I am awful with language semantics.) >> >> On 9/11/17 2:48 PM, Andrea Giammarchi wrote: >> 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 <mailto:sphink at gmail.com <mailto:sphink at gmail.com>> 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 <mailto:andrea.giammarchi at gmail.com <mailto:andrea.giammarchi at gmail.com>> 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 <mailto:fuchsia.groan at virgin.net <mailto:fuchsia.groan at virgin.net>> 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 >> mailto:es-discuss at mozilla.org <mailto:es-discuss at mozilla.org> >> https://mail.mozilla.org/listinfo/es-discuss <https://mail.mozilla.org/listinfo/es-discuss> >> >> >> >> >> >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org <mailto:es-discuss at mozilla.org> >> https://mail.mozilla.org/listinfo/es-discuss <https://mail.mozilla.org/listinfo/es-discuss> > > > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org <mailto:es-discuss at mozilla.org> > https://mail.mozilla.org/listinfo/es-discuss <https://mail.mozilla.org/listinfo/es-discuss> > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170916/fe1741df/attachment-0001.html>
there's nothing bad in using accessors (getters/setters) but you can do what you want, just don't tell everyone what you do is the only way 'cause it's not.
I'm off this thread, there's probably nothing else to add.
Best
there's nothing bad in using accessors (getters/setters) but you can do what you want, just don't tell everyone what you do is the only way 'cause it's not. I'm off this thread, there's probably nothing else to add. Best Regards On Sat, Sep 16, 2017 at 9:15 AM, kai zhu <kaizhu256 at gmail.com> wrote: > you can call it dogma. i call it the the javascript-way of solving > frontend-engineering problems, which is simpler and more practical for > laymen than what i learned from cs-theory in school using c++/java. > > its time we showed some pride in javascript (like the haskell/lisp/etc > folks), and admit some of our 20+ year-old “hacks” are actually good > design-patterns that rival and challenge traditional cs-teachings, instead > of replacing them with bad-practices from other languages (*cough* > operator-overloading *cough*). > > On Sep 14, 2017, at 4:26 PM, Andrea Giammarchi < > andrea.giammarchi at gmail.com> wrote: > > > please use normal method-calls instead, which are more maintainable > > no, thanks. > > > > WeakMap's implicit-garbage-collecting feature is error-prone, hard to > validate with tests, and generally becomes unmaintainable over time. > > no, thanks. > > We were discussing patterns here, nothing to "please don't" and IMO, dogma > oriented rules are over-engineered anti-pattern too. > > Let's not make this ML an awkward place for who wants to just learn or > explore JS language features, thanks. > > > > > > On Thu, Sep 14, 2017 at 9:55 AM, kai zhu <kaizhu256 at gmail.com> wrote: > >> inline >> >> ```js >>> class A { >>> #random; >>> get random() { >>> return this.#random || >>> (this.#random = Math.random()); >>> } >>> } >>> >>> >>> // transpiled >>> var A = function (wm) { >>> function A() { >>> wm.set(this, {random: void 0}); >>> } >>> Object.defineProperties( >>> A.prototype, >>> { >>> random: { >>> configurable: true, >>> get: function () { >>> return wm.get(this).random || >>> (wm.get(this).random = Math.random()); >>> } >>> } >>> } >>> ); >>> return A; >>> }(new WeakMap); >>> ``` >> >> >> - overloaded getters/setters and Object.defineProperties are generally *over-engineered >> *anti-patterns in javascript-programming >> * please use normal method-calls instead, which are more maintainable >> >> - WeakMap is an *over-engineered *anti-pattern in javascript-programming >> * they are inferior to using a plain-object cache-dictionary in pretty >> much every use-case. >> * WeakMap's implicit-garbage-collecting feature is error-prone, hard to >> validate with tests, and generally becomes unmaintainable over time. >> >> ```javascript >> /*jslint node: true */ >> 'use strict'; >> >> function LazyClass() { >> this.cacheDict = {}; >> } >> LazyClass.prototype.getRandomCached = function () { >> this.cacheDict.random = this.cacheDict.random || Math.random(); >> return this.cacheDict.random; >> }; >> LazyClass.prototype.clearCache = function () { >> this.cacheDict = {}; >> }; >> >> var lazyObject = new LazyClass(); >> console.log(lazyObject.getRandomCached()); >> console.log(lazyObject.getRandomCached()); >> lazyObject.clearCache(); >> console.log(lazyObject.getRandomCached()); >> ``` >> >> <PastedGraphic-4.png> >> >> >> >> On Sep 13, 2017, at 9:32 PM, Andrea Giammarchi < >> andrea.giammarchi at gmail.com> wrote: >> >> > Maybe you mean "will behave more or less as if (except more >> efficiently)"? >> >> no, I meant: it will transpiled into something using private WeakMaps. >> >> I don't have any interest in talk nanoseconds for something unrelated to >> the topic though. >> >> Best Regards >> >> On Wed, Sep 13, 2017 at 1:54 PM, Alex Kodat <alexkodat at gmail.com> wrote: >> >>> What do you mean by “will be transpiled through”? My understanding of >>> the private property proposal is that private properties will be in fixed >>> slots (inaccessible outside the class) in the object so there would be no >>> WeakMap. Maybe you mean "will behave more or less as if (except more >>> efficiently)"? >>> >>> ---- >>> Alex Kodat >>> >>> From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of >>> Andrea Giammarchi >>> Sent: Wednesday, September 13, 2017 2:31 AM >>> To: Steve Fink <sphink at gmail.com> >>> Cc: es-discuss at mozilla.org >>> Subject: Re: Lazy evaluation >>> >>> > The properties already existed, so defineProperty shouldn't modify the >>> order IIUC >>> >>> well, nope. the property existed in the prototype, not in the object. >>> >>> anyway, I guess private properties, that are a possible solution, will >>> be transpiled through a WeakMap so that most likely anything discussed in >>> here won't make sense and the future code would look like the following >>> >>> ```js >>> class A { >>> #random; >>> get random() { >>> return this.#random || >>> (this.#random = Math.random()); >>> } >>> } >>> >>> >>> // transpiled >>> var A = function (wm) { >>> function A() { >>> wm.set(this, {random: void 0}); >>> } >>> Object.defineProperties( >>> A.prototype, >>> { >>> random: { >>> configurable: true, >>> get: function () { >>> return wm.get(this).random || >>> (wm.get(this).random = Math.random()); >>> } >>> } >>> } >>> ); >>> return A; >>> }(new WeakMap); >>> ``` >>> >>> >>> >>> >>> >>> >>> On Tue, Sep 12, 2017 at 10:39 PM, Steve Fink <mailto:sphink at gmail.com> >>> wrote: >>> My intent was only to respond to the performance analysis, specifically >>> the implication that the only performance cost is in building the new >>> hidden class. That is not the case; everything that touches those objects >>> is affected as well. >>> >>> Whether or not it's still the right way to accomplish what you're after, >>> I wasn't venturing an opinion. I could probably come up with a benchmark >>> showing that your WeakMap approach can be faster -- eg by only accessing >>> the property once, but feeding the old and new versions of the object into >>> code that executes many many many times (doing something that never looks >>> at that property, but is now slightly slower because it isn't monomorphic). >>> But I suspect that for practical usage, redefining the property *is* faster >>> than a WeakMap. >>> >>> If I were to look beyond for other solutions for your problem, then I'm >>> just speculating. Can decorators populate multiple properties once the >>> expensive work is done? >>> >>> I really want to tell the VM what's going on. I guess if it knew that >>> accessing a getter property would convert it into a value property, and >>> that it was doing something that would access the getter, then it could >>> know to use the outgoing shape instead of the incoming shape. If only it >>> knew that the getter was pure... but that way lies madness. >>> >>> Given that most code that would slow down would also trigger the lazy >>> defineProperty(), it's really not going to be that much of an issue. Any >>> access after the first will see a single shape. >>> >>> meh. Just take the perf hit, with awareness that you may be triggering >>> slight slowdowns in all users of that object. Or you might not. I doubt >>> it'll be that big, since you'll probably just end up with an inline cache >>> for both shapes and there won't be all that much to optimize based on >>> knowing a single shape. >>> >>> Oh, and I think I was wrong about property enumeration order. The >>> properties already existed, so defineProperty shouldn't modify the order >>> IIUC. (I am awful with language semantics.) >>> >>> On 9/11/17 2:48 PM, Andrea Giammarchi wrote: >>> 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 <mailto:sphink at gmail.com> >>> 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 <mailto: >>> andrea.giammarchi at gmail.com> 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 <mailto: >>> fuchsia.groan at virgin.net> 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 >>> mailto:es-discuss at mozilla.org >>> https://mail.mozilla.org/listinfo/es-discuss >>> >>> >>> >>> >>> >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss >> >> >> >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss >> >> > > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170916/ca890c48/attachment-0001.html>
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.
As for what would be a nice API, maybe something like one of these?
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:const x = lazy do { ... } // expose via `x.get()` or just `x()`
Isiah Meadows me at isiahmeadows.com
Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com