proposal: Object Members
class
is already not just syntactic sugar, so that notion isn't correct,
and shouldn't be maintained.
`class` is already not just syntactic sugar, so that notion isn't correct, and shouldn't be maintained. On Mon, Jul 23, 2018 at 12:38 PM, Ranando King <kingmph at gmail.com> wrote: > I've written up a new draft proposal based on my own work with ES5 & ES6 > compatible classes with fields. That can be found [here]( > https://github.com/rdking/proposal-object-members). I'm already aware of > the class-members proposal, but I think it breaks far to many things and > doesn't do anything to maintain the notion that "`class` is just syntactic > sugar". > > This proposal is specifically based on the code [here](https://github.com/ > rdking/Class.js/tree/master/es6c). I've also got a [repl.it]( > https://repl.it/@arkain/Classjs-Compact-Syntax-ES6) that shows the same > code running. > > The idea behind the proposal is that instead of injecting a lot of new > logic into how `class` works, let's allow `class` to remain syntactic > sugar, and put that extra ability into object declarations instead. Then > simply allow `class` to do the same with it's own prototypes. > > _______________________________________________ > 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/20180723/489a87c2/attachment.html>
---------- Forwarded message --------- From: Ranando King <kingmph at gmail.com>
Date: Mon, Jul 23, 2018 at 4:04 PM Subject: Re: proposal: Object Members To: <ljharb at gmail.com>
You've made that argument before. Exactly what is it in ES6 that you
can do with class
that you cannot do without class? I'd like some
clarification on this.
---------- Forwarded message --------- From: Ranando King <kingmph at gmail.com> Date: Mon, Jul 23, 2018 at 4:04 PM Subject: Re: proposal: Object Members To: <ljharb at gmail.com> You've made that argument before. Exactly what is it in ES6 that you **can** do with `class` that you cannot do without class? I'd like some clarification on this. On Mon, Jul 23, 2018 at 3:30 PM Jordan Harband <ljharb at gmail.com> wrote: > `class` is already not just syntactic sugar, so that notion isn't correct, > and shouldn't be maintained. > > On Mon, Jul 23, 2018 at 12:38 PM, Ranando King <kingmph at gmail.com> wrote: > >> I've written up a new draft proposal based on my own work with ES5 & ES6 >> compatible classes with fields. That can be found [here]( >> https://github.com/rdking/proposal-object-members). I'm already aware of >> the class-members proposal, but I think it breaks far to many things and >> doesn't do anything to maintain the notion that "`class` is just syntactic >> sugar". >> >> This proposal is specifically based on the code [here]( >> https://github.com/rdking/Class.js/tree/master/es6c). I've also got a [ >> repl.it](https://repl.it/@arkain/Classjs-Compact-Syntax-ES6) that shows >> the same code running. >> >> The idea behind the proposal is that instead of injecting a lot of new >> logic into how `class` works, let's allow `class` to remain syntactic >> sugar, and put that extra ability into object declarations instead. Then >> simply allow `class` to do the same with it's own prototypes. >> >> _______________________________________________ >> 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/20180723/b21e7ccf/attachment.html>
Extend builtins, in particular - ie, super()
allows your subclass to
obtain internal slots it can't otherwise get.
Even if class
were just sugar, I don't think I see the argument that
that's a good thing to preserve.
Extend builtins, in particular - ie, `super()` allows your subclass to obtain internal slots it can't otherwise get. Even if `class` were just sugar, I don't think I see the argument that that's a *good* thing to preserve. On Mon, Jul 23, 2018 at 2:05 PM, Ranando King <kingmph at gmail.com> wrote: > > > ---------- Forwarded message --------- > From: Ranando King <kingmph at gmail.com> > Date: Mon, Jul 23, 2018 at 4:04 PM > Subject: Re: proposal: Object Members > To: <ljharb at gmail.com> > > > You've made that argument before. Exactly what is it in ES6 that you > **can** do with `class` that you cannot do without class? I'd like some > clarification on this. > > On Mon, Jul 23, 2018 at 3:30 PM Jordan Harband <ljharb at gmail.com> wrote: > >> `class` is already not just syntactic sugar, so that notion isn't >> correct, and shouldn't be maintained. >> >> On Mon, Jul 23, 2018 at 12:38 PM, Ranando King <kingmph at gmail.com> wrote: >> >>> I've written up a new draft proposal based on my own work with ES5 & ES6 >>> compatible classes with fields. That can be found [here]( >>> https://github.com/rdking/proposal-object-members). I'm already aware >>> of the class-members proposal, but I think it breaks far to many things and >>> doesn't do anything to maintain the notion that "`class` is just syntactic >>> sugar". >>> >>> This proposal is specifically based on the code [here]( >>> https://github.com/rdking/Class.js/tree/master/es6c). I've also got a [ >>> repl.it](https://repl.it/@arkain/Classjs-Compact-Syntax-ES6) that shows >>> the same code running. >>> >>> The idea behind the proposal is that instead of injecting a lot of new >>> logic into how `class` works, let's allow `class` to remain syntactic >>> sugar, and put that extra ability into object declarations instead. Then >>> simply allow `class` to do the same with it's own prototypes. >>> >>> _______________________________________________ >>> 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/20180723/29a9bbfc/attachment-0001.html>
Granted about super()
. That's the one thing I can't easily reproduce.
However, barring those internal slots, I can reproduce the functionality of
super
and the checks performed as a result of the internal slots, all in
ES6. As for built-ins, I can easily and properly extend builtins without
class
since ES6 officially has Object.setPrototypeOf()
. If you don't
think it's possible, you should take a close look at what I'm doing in the
repl.it link from my first post.
As for whether or not the sugary nature of class
is a good thing, it
really is a matter of opinion. I just happen to be of the persuasion that
since there's literally no construct that class
can produce that I cannot
reproduce by other means, then that means the class
keyword (even in
light of super
) is little more than syntactic sugar. As such, we
shouldn't be so hasty to turn an Object Oriented Prototype Based language
into an Object Oriented Class Based language. The only way to do that
reasonably is to ensure that whatever you can construct with class
can
always be equivalently constructed without it.
Here's a more logical argument instead. Even if there are subtle
differences between class
constructors and object factory functions,
providing an isolated path specific to class
is likely to lead to
situations very similar to what happens when an open source package gets
forked. Eventually, the difference between the two paths may become so
great that one is eventually abandoned (by developers) in favor of the
other. This is only a valid argument because the power of ES is in it's
simplicity. It's like building a house with wood, nails, sheetrock, etc...
(JS) vs. building a house with pre-fabricated parts (class-based languages).
Don't get me wrong. The class
keyword is a great thing. It simplifies the
production of creating object factories with prototypes. As I understand
it, that was the purpose. Let's not make the mistake of allowing something
to be done with class
that cannot be reasonably reproduced without it.
The moment we do that, we're diverging from the intended purpose of class
.
Granted about `super()`. That's the one thing I can't easily reproduce. However, barring those internal slots, I can reproduce the functionality of `super` and the checks performed as a result of the internal slots, all in ES6. As for built-ins, I can easily and properly extend builtins without `class` since ES6 officially has `Object.setPrototypeOf()`. If you don't think it's possible, you should take a close look at what I'm doing in the repl.it link from my first post. As for whether or not the sugary nature of `class` is a good thing, it really is a matter of opinion. I just happen to be of the persuasion that since there's literally no construct that `class` can produce that I cannot reproduce by other means, then that means the `class` keyword (even in light of `super`) is little more than syntactic sugar. As such, we shouldn't be so hasty to turn an Object Oriented Prototype Based language into an Object Oriented Class Based language. The only way to do that reasonably is to ensure that whatever you can construct with `class` can always be equivalently constructed without it. Here's a more logical argument instead. Even if there are subtle differences between `class` constructors and object factory functions, providing an isolated path specific to `class` is likely to lead to situations very similar to what happens when an open source package gets forked. Eventually, the difference between the two paths may become so great that one is eventually abandoned (by developers) in favor of the other. This is only a valid argument because the power of ES is in it's simplicity. It's like building a house with wood, nails, sheetrock, etc... (JS) vs. building a house with pre-fabricated parts (class-based languages). Don't get me wrong. The `class` keyword is a great thing. It simplifies the production of creating object factories with prototypes. As I understand it, that was the purpose. Let's not make the mistake of allowing something to be done with `class` that cannot be reasonably reproduced without it. The moment we do that, we're diverging from the intended purpose of `class`. On Mon, Jul 23, 2018 at 4:17 PM Jordan Harband <ljharb at gmail.com> wrote: > Extend builtins, in particular - ie, `super()` allows your subclass to > obtain internal slots it can't otherwise get. > > Even if `class` were just sugar, I don't think I see the argument that > that's a *good* thing to preserve. > > On Mon, Jul 23, 2018 at 2:05 PM, Ranando King <kingmph at gmail.com> wrote: > >> >> >> ---------- Forwarded message --------- >> From: Ranando King <kingmph at gmail.com> >> Date: Mon, Jul 23, 2018 at 4:04 PM >> Subject: Re: proposal: Object Members >> To: <ljharb at gmail.com> >> >> >> You've made that argument before. Exactly what is it in ES6 that you >> **can** do with `class` that you cannot do without class? I'd like some >> clarification on this. >> >> On Mon, Jul 23, 2018 at 3:30 PM Jordan Harband <ljharb at gmail.com> wrote: >> >>> `class` is already not just syntactic sugar, so that notion isn't >>> correct, and shouldn't be maintained. >>> >>> On Mon, Jul 23, 2018 at 12:38 PM, Ranando King <kingmph at gmail.com> >>> wrote: >>> >>>> I've written up a new draft proposal based on my own work with ES5 & >>>> ES6 compatible classes with fields. That can be found [here]( >>>> https://github.com/rdking/proposal-object-members). I'm already aware >>>> of the class-members proposal, but I think it breaks far to many things and >>>> doesn't do anything to maintain the notion that "`class` is just syntactic >>>> sugar". >>>> >>>> This proposal is specifically based on the code [here]( >>>> https://github.com/rdking/Class.js/tree/master/es6c). I've also got a [ >>>> repl.it](https://repl.it/@arkain/Classjs-Compact-Syntax-ES6) that >>>> shows the same code running. >>>> >>>> The idea behind the proposal is that instead of injecting a lot of new >>>> logic into how `class` works, let's allow `class` to remain syntactic >>>> sugar, and put that extra ability into object declarations instead. Then >>>> simply allow `class` to do the same with it's own prototypes. >>>> >>>> _______________________________________________ >>>> 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/20180723/886fdb76/attachment.html>
What exactly can be accomplished with super that can't be accomplished otherwise? I know the transpiled code is very verbose and unintuitive to read if you avoid explicitly naming the base class, but I wasn't aware of new capabilities that were previously impossible.
Ben
Le lun. 23 juill. 2018 18 h 06, Ranando King <kingmph at gmail.com> a écrit :
What exactly can be accomplished with super that can't be accomplished otherwise? I know the transpiled code is very verbose and unintuitive to read if you avoid explicitly naming the base class, but I wasn't aware of new capabilities that were previously impossible. Ben Le lun. 23 juill. 2018 18 h 06, Ranando King <kingmph at gmail.com> a écrit : > Granted about `super()`. That's the one thing I can't easily reproduce. > However, barring those internal slots, I can reproduce the functionality of > `super` and the checks performed as a result of the internal slots, all in > ES6. As for built-ins, I can easily and properly extend builtins without > `class` since ES6 officially has `Object.setPrototypeOf()`. If you don't > think it's possible, you should take a close look at what I'm doing in the > repl.it link from my first post. > > As for whether or not the sugary nature of `class` is a good thing, it > really is a matter of opinion. I just happen to be of the persuasion that > since there's literally no construct that `class` can produce that I cannot > reproduce by other means, then that means the `class` keyword (even in > light of `super`) is little more than syntactic sugar. As such, we > shouldn't be so hasty to turn an Object Oriented Prototype Based language > into an Object Oriented Class Based language. The only way to do that > reasonably is to ensure that whatever you can construct with `class` can > always be equivalently constructed without it. > > Here's a more logical argument instead. Even if there are subtle > differences between `class` constructors and object factory functions, > providing an isolated path specific to `class` is likely to lead to > situations very similar to what happens when an open source package gets > forked. Eventually, the difference between the two paths may become so > great that one is eventually abandoned (by developers) in favor of the > other. This is only a valid argument because the power of ES is in it's > simplicity. It's like building a house with wood, nails, sheetrock, etc... > (JS) vs. building a house with pre-fabricated parts (class-based languages). > > Don't get me wrong. The `class` keyword is a great thing. It simplifies > the production of creating object factories with prototypes. As I > understand it, that was the purpose. Let's not make the mistake of allowing > something to be done with `class` that cannot be reasonably reproduced > without it. The moment we do that, we're diverging from the intended > purpose of `class`. > > > > On Mon, Jul 23, 2018 at 4:17 PM Jordan Harband <ljharb at gmail.com> wrote: > >> Extend builtins, in particular - ie, `super()` allows your subclass to >> obtain internal slots it can't otherwise get. >> >> Even if `class` were just sugar, I don't think I see the argument that >> that's a *good* thing to preserve. >> >> On Mon, Jul 23, 2018 at 2:05 PM, Ranando King <kingmph at gmail.com> wrote: >> >>> >>> >>> ---------- Forwarded message --------- >>> From: Ranando King <kingmph at gmail.com> >>> Date: Mon, Jul 23, 2018 at 4:04 PM >>> Subject: Re: proposal: Object Members >>> To: <ljharb at gmail.com> >>> >>> >>> You've made that argument before. Exactly what is it in ES6 that you >>> **can** do with `class` that you cannot do without class? I'd like some >>> clarification on this. >>> >>> On Mon, Jul 23, 2018 at 3:30 PM Jordan Harband <ljharb at gmail.com> wrote: >>> >>>> `class` is already not just syntactic sugar, so that notion isn't >>>> correct, and shouldn't be maintained. >>>> >>>> On Mon, Jul 23, 2018 at 12:38 PM, Ranando King <kingmph at gmail.com> >>>> wrote: >>>> >>>>> I've written up a new draft proposal based on my own work with ES5 & >>>>> ES6 compatible classes with fields. That can be found [here]( >>>>> https://github.com/rdking/proposal-object-members). I'm already aware >>>>> of the class-members proposal, but I think it breaks far to many things and >>>>> doesn't do anything to maintain the notion that "`class` is just syntactic >>>>> sugar". >>>>> >>>>> This proposal is specifically based on the code [here]( >>>>> https://github.com/rdking/Class.js/tree/master/es6c). I've also got a >>>>> [repl.it](https://repl.it/@arkain/Classjs-Compact-Syntax-ES6) that >>>>> shows the same code running. >>>>> >>>>> The idea behind the proposal is that instead of injecting a lot of new >>>>> logic into how `class` works, let's allow `class` to remain syntactic >>>>> sugar, and put that extra ability into object declarations instead. Then >>>>> simply allow `class` to do the same with it's own prototypes. >>>>> >>>>> _______________________________________________ >>>>> 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/20180723/861fe1bd/attachment-0001.html>
When extending builtins, super()
is the only way you can get the
appropriate internal slots applied to the instance. (Private fields work
the same way by providing a matching guarantee - that the only way someone
can subclass you successfully is using class extends
and super
)
When extending builtins, `super()` is the only way you can get the appropriate internal slots applied to the instance. (Private fields work the same way by providing a matching guarantee - that the only way someone can subclass you successfully is using `class extends` and `super`) On Mon, Jul 23, 2018 at 3:43 PM, Ben Wiley <therealbenwiley at gmail.com> wrote: > What exactly can be accomplished with super that can't be accomplished > otherwise? I know the transpiled code is very verbose and unintuitive to > read if you avoid explicitly naming the base class, but I wasn't aware of > new capabilities that were previously impossible. > > Ben > > > Le lun. 23 juill. 2018 18 h 06, Ranando King <kingmph at gmail.com> a écrit : > >> Granted about `super()`. That's the one thing I can't easily reproduce. >> However, barring those internal slots, I can reproduce the functionality of >> `super` and the checks performed as a result of the internal slots, all in >> ES6. As for built-ins, I can easily and properly extend builtins without >> `class` since ES6 officially has `Object.setPrototypeOf()`. If you don't >> think it's possible, you should take a close look at what I'm doing in the >> repl.it link from my first post. >> >> As for whether or not the sugary nature of `class` is a good thing, it >> really is a matter of opinion. I just happen to be of the persuasion that >> since there's literally no construct that `class` can produce that I cannot >> reproduce by other means, then that means the `class` keyword (even in >> light of `super`) is little more than syntactic sugar. As such, we >> shouldn't be so hasty to turn an Object Oriented Prototype Based language >> into an Object Oriented Class Based language. The only way to do that >> reasonably is to ensure that whatever you can construct with `class` can >> always be equivalently constructed without it. >> >> Here's a more logical argument instead. Even if there are subtle >> differences between `class` constructors and object factory functions, >> providing an isolated path specific to `class` is likely to lead to >> situations very similar to what happens when an open source package gets >> forked. Eventually, the difference between the two paths may become so >> great that one is eventually abandoned (by developers) in favor of the >> other. This is only a valid argument because the power of ES is in it's >> simplicity. It's like building a house with wood, nails, sheetrock, etc... >> (JS) vs. building a house with pre-fabricated parts (class-based languages). >> >> Don't get me wrong. The `class` keyword is a great thing. It simplifies >> the production of creating object factories with prototypes. As I >> understand it, that was the purpose. Let's not make the mistake of allowing >> something to be done with `class` that cannot be reasonably reproduced >> without it. The moment we do that, we're diverging from the intended >> purpose of `class`. >> >> >> >> On Mon, Jul 23, 2018 at 4:17 PM Jordan Harband <ljharb at gmail.com> wrote: >> >>> Extend builtins, in particular - ie, `super()` allows your subclass to >>> obtain internal slots it can't otherwise get. >>> >>> Even if `class` were just sugar, I don't think I see the argument that >>> that's a *good* thing to preserve. >>> >>> On Mon, Jul 23, 2018 at 2:05 PM, Ranando King <kingmph at gmail.com> wrote: >>> >>>> >>>> >>>> ---------- Forwarded message --------- >>>> From: Ranando King <kingmph at gmail.com> >>>> Date: Mon, Jul 23, 2018 at 4:04 PM >>>> Subject: Re: proposal: Object Members >>>> To: <ljharb at gmail.com> >>>> >>>> >>>> You've made that argument before. Exactly what is it in ES6 that you >>>> **can** do with `class` that you cannot do without class? I'd like some >>>> clarification on this. >>>> >>>> On Mon, Jul 23, 2018 at 3:30 PM Jordan Harband <ljharb at gmail.com> >>>> wrote: >>>> >>>>> `class` is already not just syntactic sugar, so that notion isn't >>>>> correct, and shouldn't be maintained. >>>>> >>>>> On Mon, Jul 23, 2018 at 12:38 PM, Ranando King <kingmph at gmail.com> >>>>> wrote: >>>>> >>>>>> I've written up a new draft proposal based on my own work with ES5 & >>>>>> ES6 compatible classes with fields. That can be found [here]( >>>>>> https://github.com/rdking/proposal-object-members). I'm already >>>>>> aware of the class-members proposal, but I think it breaks far to many >>>>>> things and doesn't do anything to maintain the notion that "`class` is just >>>>>> syntactic sugar". >>>>>> >>>>>> This proposal is specifically based on the code [here]( >>>>>> https://github.com/rdking/Class.js/tree/master/es6c). I've also got >>>>>> a [repl.it](https://repl.it/@arkain/Classjs-Compact-Syntax-ES6) that >>>>>> shows the same code running. >>>>>> >>>>>> The idea behind the proposal is that instead of injecting a lot of >>>>>> new logic into how `class` works, let's allow `class` to remain syntactic >>>>>> sugar, and put that extra ability into object declarations instead. Then >>>>>> simply allow `class` to do the same with it's own prototypes. >>>>>> >>>>>> _______________________________________________ >>>>>> 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/20180723/6b89a16c/attachment.html>
I see, so it's not that you can't do things without class as much as you can impose limitations by using class. Thanks for clarifying
Le lun. 23 juill. 2018 18 h 49, Jordan Harband <ljharb at gmail.com> a écrit :
I see, so it's not that you can't do things without class as much as you can impose limitations by using class. Thanks for clarifying Le lun. 23 juill. 2018 18 h 49, Jordan Harband <ljharb at gmail.com> a écrit : > When extending builtins, `super()` is the only way you can get the > appropriate internal slots applied to the instance. (Private fields work > the same way by providing a matching guarantee - that the only way someone > can subclass you successfully is using `class extends` and `super`) > > On Mon, Jul 23, 2018 at 3:43 PM, Ben Wiley <therealbenwiley at gmail.com> > wrote: > >> What exactly can be accomplished with super that can't be accomplished >> otherwise? I know the transpiled code is very verbose and unintuitive to >> read if you avoid explicitly naming the base class, but I wasn't aware of >> new capabilities that were previously impossible. >> >> Ben >> >> >> Le lun. 23 juill. 2018 18 h 06, Ranando King <kingmph at gmail.com> a >> écrit : >> >>> Granted about `super()`. That's the one thing I can't easily reproduce. >>> However, barring those internal slots, I can reproduce the functionality of >>> `super` and the checks performed as a result of the internal slots, all in >>> ES6. As for built-ins, I can easily and properly extend builtins without >>> `class` since ES6 officially has `Object.setPrototypeOf()`. If you don't >>> think it's possible, you should take a close look at what I'm doing in the >>> repl.it link from my first post. >>> >>> As for whether or not the sugary nature of `class` is a good thing, it >>> really is a matter of opinion. I just happen to be of the persuasion that >>> since there's literally no construct that `class` can produce that I cannot >>> reproduce by other means, then that means the `class` keyword (even in >>> light of `super`) is little more than syntactic sugar. As such, we >>> shouldn't be so hasty to turn an Object Oriented Prototype Based language >>> into an Object Oriented Class Based language. The only way to do that >>> reasonably is to ensure that whatever you can construct with `class` can >>> always be equivalently constructed without it. >>> >>> Here's a more logical argument instead. Even if there are subtle >>> differences between `class` constructors and object factory functions, >>> providing an isolated path specific to `class` is likely to lead to >>> situations very similar to what happens when an open source package gets >>> forked. Eventually, the difference between the two paths may become so >>> great that one is eventually abandoned (by developers) in favor of the >>> other. This is only a valid argument because the power of ES is in it's >>> simplicity. It's like building a house with wood, nails, sheetrock, etc... >>> (JS) vs. building a house with pre-fabricated parts (class-based languages). >>> >>> Don't get me wrong. The `class` keyword is a great thing. It simplifies >>> the production of creating object factories with prototypes. As I >>> understand it, that was the purpose. Let's not make the mistake of allowing >>> something to be done with `class` that cannot be reasonably reproduced >>> without it. The moment we do that, we're diverging from the intended >>> purpose of `class`. >>> >>> >>> >>> On Mon, Jul 23, 2018 at 4:17 PM Jordan Harband <ljharb at gmail.com> wrote: >>> >>>> Extend builtins, in particular - ie, `super()` allows your subclass to >>>> obtain internal slots it can't otherwise get. >>>> >>>> Even if `class` were just sugar, I don't think I see the argument that >>>> that's a *good* thing to preserve. >>>> >>>> On Mon, Jul 23, 2018 at 2:05 PM, Ranando King <kingmph at gmail.com> >>>> wrote: >>>> >>>>> >>>>> >>>>> ---------- Forwarded message --------- >>>>> From: Ranando King <kingmph at gmail.com> >>>>> Date: Mon, Jul 23, 2018 at 4:04 PM >>>>> Subject: Re: proposal: Object Members >>>>> To: <ljharb at gmail.com> >>>>> >>>>> >>>>> You've made that argument before. Exactly what is it in ES6 that you >>>>> **can** do with `class` that you cannot do without class? I'd like some >>>>> clarification on this. >>>>> >>>>> On Mon, Jul 23, 2018 at 3:30 PM Jordan Harband <ljharb at gmail.com> >>>>> wrote: >>>>> >>>>>> `class` is already not just syntactic sugar, so that notion isn't >>>>>> correct, and shouldn't be maintained. >>>>>> >>>>>> On Mon, Jul 23, 2018 at 12:38 PM, Ranando King <kingmph at gmail.com> >>>>>> wrote: >>>>>> >>>>>>> I've written up a new draft proposal based on my own work with ES5 & >>>>>>> ES6 compatible classes with fields. That can be found [here]( >>>>>>> https://github.com/rdking/proposal-object-members). I'm already >>>>>>> aware of the class-members proposal, but I think it breaks far to many >>>>>>> things and doesn't do anything to maintain the notion that "`class` is just >>>>>>> syntactic sugar". >>>>>>> >>>>>>> This proposal is specifically based on the code [here]( >>>>>>> https://github.com/rdking/Class.js/tree/master/es6c). I've also got >>>>>>> a [repl.it](https://repl.it/@arkain/Classjs-Compact-Syntax-ES6) >>>>>>> that shows the same code running. >>>>>>> >>>>>>> The idea behind the proposal is that instead of injecting a lot of >>>>>>> new logic into how `class` works, let's allow `class` to remain syntactic >>>>>>> sugar, and put that extra ability into object declarations instead. Then >>>>>>> simply allow `class` to do the same with it's own prototypes. >>>>>>> >>>>>>> _______________________________________________ >>>>>>> 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/20180723/92e055a6/attachment-0001.html>
That, and that the existing builtins already impose those limitations - and
only class
allows you to do those things, with them.
That, and that the existing builtins already impose those limitations - and only `class` allows you to do those things, with them. On Mon, Jul 23, 2018 at 3:52 PM, Ben Wiley <therealbenwiley at gmail.com> wrote: > I see, so it's not that you can't do things without class as much as you > can impose limitations by using class. Thanks for clarifying > > Le lun. 23 juill. 2018 18 h 49, Jordan Harband <ljharb at gmail.com> a > écrit : > >> When extending builtins, `super()` is the only way you can get the >> appropriate internal slots applied to the instance. (Private fields work >> the same way by providing a matching guarantee - that the only way someone >> can subclass you successfully is using `class extends` and `super`) >> >> On Mon, Jul 23, 2018 at 3:43 PM, Ben Wiley <therealbenwiley at gmail.com> >> wrote: >> >>> What exactly can be accomplished with super that can't be accomplished >>> otherwise? I know the transpiled code is very verbose and unintuitive to >>> read if you avoid explicitly naming the base class, but I wasn't aware of >>> new capabilities that were previously impossible. >>> >>> Ben >>> >>> >>> Le lun. 23 juill. 2018 18 h 06, Ranando King <kingmph at gmail.com> a >>> écrit : >>> >>>> Granted about `super()`. That's the one thing I can't easily reproduce. >>>> However, barring those internal slots, I can reproduce the functionality of >>>> `super` and the checks performed as a result of the internal slots, all in >>>> ES6. As for built-ins, I can easily and properly extend builtins without >>>> `class` since ES6 officially has `Object.setPrototypeOf()`. If you don't >>>> think it's possible, you should take a close look at what I'm doing in the >>>> repl.it link from my first post. >>>> >>>> As for whether or not the sugary nature of `class` is a good thing, it >>>> really is a matter of opinion. I just happen to be of the persuasion that >>>> since there's literally no construct that `class` can produce that I cannot >>>> reproduce by other means, then that means the `class` keyword (even in >>>> light of `super`) is little more than syntactic sugar. As such, we >>>> shouldn't be so hasty to turn an Object Oriented Prototype Based language >>>> into an Object Oriented Class Based language. The only way to do that >>>> reasonably is to ensure that whatever you can construct with `class` can >>>> always be equivalently constructed without it. >>>> >>>> Here's a more logical argument instead. Even if there are subtle >>>> differences between `class` constructors and object factory functions, >>>> providing an isolated path specific to `class` is likely to lead to >>>> situations very similar to what happens when an open source package gets >>>> forked. Eventually, the difference between the two paths may become so >>>> great that one is eventually abandoned (by developers) in favor of the >>>> other. This is only a valid argument because the power of ES is in it's >>>> simplicity. It's like building a house with wood, nails, sheetrock, etc... >>>> (JS) vs. building a house with pre-fabricated parts (class-based languages). >>>> >>>> Don't get me wrong. The `class` keyword is a great thing. It simplifies >>>> the production of creating object factories with prototypes. As I >>>> understand it, that was the purpose. Let's not make the mistake of allowing >>>> something to be done with `class` that cannot be reasonably reproduced >>>> without it. The moment we do that, we're diverging from the intended >>>> purpose of `class`. >>>> >>>> >>>> >>>> On Mon, Jul 23, 2018 at 4:17 PM Jordan Harband <ljharb at gmail.com> >>>> wrote: >>>> >>>>> Extend builtins, in particular - ie, `super()` allows your subclass to >>>>> obtain internal slots it can't otherwise get. >>>>> >>>>> Even if `class` were just sugar, I don't think I see the argument that >>>>> that's a *good* thing to preserve. >>>>> >>>>> On Mon, Jul 23, 2018 at 2:05 PM, Ranando King <kingmph at gmail.com> >>>>> wrote: >>>>> >>>>>> >>>>>> >>>>>> ---------- Forwarded message --------- >>>>>> From: Ranando King <kingmph at gmail.com> >>>>>> Date: Mon, Jul 23, 2018 at 4:04 PM >>>>>> Subject: Re: proposal: Object Members >>>>>> To: <ljharb at gmail.com> >>>>>> >>>>>> >>>>>> You've made that argument before. Exactly what is it in ES6 that you >>>>>> **can** do with `class` that you cannot do without class? I'd like some >>>>>> clarification on this. >>>>>> >>>>>> On Mon, Jul 23, 2018 at 3:30 PM Jordan Harband <ljharb at gmail.com> >>>>>> wrote: >>>>>> >>>>>>> `class` is already not just syntactic sugar, so that notion isn't >>>>>>> correct, and shouldn't be maintained. >>>>>>> >>>>>>> On Mon, Jul 23, 2018 at 12:38 PM, Ranando King <kingmph at gmail.com> >>>>>>> wrote: >>>>>>> >>>>>>>> I've written up a new draft proposal based on my own work with ES5 >>>>>>>> & ES6 compatible classes with fields. That can be found [here]( >>>>>>>> https://github.com/rdking/proposal-object-members). I'm already >>>>>>>> aware of the class-members proposal, but I think it breaks far to many >>>>>>>> things and doesn't do anything to maintain the notion that "`class` is just >>>>>>>> syntactic sugar". >>>>>>>> >>>>>>>> This proposal is specifically based on the code [here]( >>>>>>>> https://github.com/rdking/Class.js/tree/master/es6c). I've also >>>>>>>> got a [repl.it](https://repl.it/@arkain/Classjs-Compact-Syntax-ES6) >>>>>>>> that shows the same code running. >>>>>>>> >>>>>>>> The idea behind the proposal is that instead of injecting a lot of >>>>>>>> new logic into how `class` works, let's allow `class` to remain syntactic >>>>>>>> sugar, and put that extra ability into object declarations instead. Then >>>>>>>> simply allow `class` to do the same with it's own prototypes. >>>>>>>> >>>>>>>> _______________________________________________ >>>>>>>> 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/20180723/e84479d3/attachment.html>
That, and that the existing builtins already impose those limitations - and only class allows you to do those things, with them.
I’m surprised by that statement — it appeared to me that it currently remains possible to create classes, including classes that extend built-ins, without class syntax.
const test = value => {
if (typeof value !== 'string') throw new TypeError('nope');
return value;
};
function StringSet(init) {
if (new.target === undefined)
throw new TypeError('StringSet is not a constructor');
if (init !== undefined)
init = Array.from(init, test);
return Reflect.construct(Set, [ init ], new.target);
}
Reflect.defineProperty(StringSet.prototype, 'add', {
configurable: true,
value: Object.setPrototypeOf({
add(value) {
return super.add(test(value));
}
}, Set.prototype).add
});
Reflect.defineProperty(StringSet.prototype, Symbol.toStringTag, {
configurable: true,
value: 'StringSet'
});
Reflect.setPrototypeOf(StringSet, Set);
Reflect.setPrototypeOf(StringSet.prototype, Set.prototype);
new StringSet('abc');
Not that this is something one is apt to want to do normally, though it being possible does remain useful sometimes for meta/compositiony stuff. It cannot be achieved in an <= ES5 environment, but it seems all the reflection tools needed are present in environments that actually have class syntax — even, despite the awkwardness necessitated by HomeObject stuff, what is needed to employ super in methods.
I’m curious if, aside from the possibility of implementation-specific things like type error messages being different, there there is anything about the above class which ends up observably different from the ES-side from one created with class syntax instead?
Edit: I can think of one. Because of how the home object’s prototype has been set for add
here, reassigning the prototype of StringSet.prototype
will not affect the super reference resolution as it would in a class-syntax class. This can be very awkwardly addressed too, though:
Reflect.defineProperty(StringSet.prototype, 'add', {
configurable: true,
value: {
add(value) {
return Reflect.getPrototypeOf(Reflect.getPrototypeOf(this)).add.call(this, test(value));
}
}.add
});
(To clarify I’m not obv not suggesting this is better than class StringSet extends Set { ... }
, just want to understand what can’t presently be done other ways.)
> That, and that the existing builtins already impose those limitations - and only class allows you to do those things, with them. I’m surprised by that statement — it appeared to me that it currently remains possible to create classes, including classes that extend built-ins, with class syntax. ``` const test = value => { if (typeof value !== 'string') throw new TypeError('nope'); return value; }; function StringSet(init) { if (new.target === undefined) throw new TypeError('StringSet is not a constructor'); if (init !== undefined) init = Array.from(init, test); return Reflect.construct(Set, [ init ], new.target); } Reflect.defineProperty(StringSet.prototype, 'add', { configurable: true, value: Object.setPrototypeOf({ add(value) { return super.add(test(value)); } }, Set.prototype).add }); Reflect.defineProperty(StringSet.prototype, Symbol.toStringTag, { configurable: true, value: 'StringSet' }); Reflect.setPrototypeOf(StringSet, Set); Reflect.setPrototypeOf(StringSet.prototype, Set.prototype); new StringSet('abc'); ``` Not that this is something one is apt to want to do normally, though it being possible does remain useful sometimes for meta/compositiony stuff. It cannot be achieved in an <= ES5 environment, but it seems all the reflection tools needed are present in environments that actually have class syntax — even, despite the awkwardness necessitated by HomeObject stuff, what is needed to employ super in methods. I’m curious if, aside from the possibility of implementation-specific things like type error messages being different, there there is anything about the above class which ends up observably different from the ES-side from one created with class syntax instead? -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180723/45ab7c61/attachment.html>
Extend builtins, in particular - ie,
super()
allows your subclass to obtain internal slots it can't otherwise get.
does extending builtins in the javascript-language even make sense, given the dominant industry application of it in a web-context? as i've said before, the primary industry-painpoints with javascript are integration-related, namely serializing/reconstructing JSON data between client <-> server. whatever low-level benefit you gain from extending builtins, typically is not worth the extra high-level integration-cost of serializing/reconstructing these custom data-structures to/from JSON.
p.s. - as an aside, new primitives like BigInt should have focused more on ease-of-use with JSON-serialization. BigInt’s primary use-case in industry as i see it, is as a mechanism for JSON-serializing 64-bit integers between client <-> server <-> database.
kai zhu kaizhu256 at gmail.com
> Extend builtins, in particular - ie, `super()` allows your subclass to obtain internal slots it can't otherwise get. does extending builtins in the javascript-language even make sense, given the dominant industry application of it in a web-context? as i've said before, the primary industry-painpoints with javascript are integration-related, namely serializing/reconstructing JSON data between client <-> server. whatever low-level benefit you gain from extending builtins, typically is not worth the extra high-level integration-cost of serializing/reconstructing these custom data-structures to/from JSON. p.s. - as an aside, new primitives like BigInt should have focused more on ease-of-use with JSON-serialization. BigInt’s primary use-case in industry as i see it, is as a mechanism for JSON-serializing 64-bit integers between client <-> server <-> database. kai zhu kaizhu256 at gmail.com > On 24 Jul 2018, at 5:56 AM, Jordan Harband <ljharb at gmail.com> wrote: > > That, and that the existing builtins already impose those limitations - and only `class` allows you to do those things, with them. > > On Mon, Jul 23, 2018 at 3:52 PM, Ben Wiley <therealbenwiley at gmail.com <mailto:therealbenwiley at gmail.com>> wrote: > I see, so it's not that you can't do things without class as much as you can impose limitations by using class. Thanks for clarifying > > Le lun. 23 juill. 2018 18 h 49, Jordan Harband <ljharb at gmail.com <mailto:ljharb at gmail.com>> a écrit : > When extending builtins, `super()` is the only way you can get the appropriate internal slots applied to the instance. (Private fields work the same way by providing a matching guarantee - that the only way someone can subclass you successfully is using `class extends` and `super`) > > On Mon, Jul 23, 2018 at 3:43 PM, Ben Wiley <therealbenwiley at gmail.com <mailto:therealbenwiley at gmail.com>> wrote: > What exactly can be accomplished with super that can't be accomplished otherwise? I know the transpiled code is very verbose and unintuitive to read if you avoid explicitly naming the base class, but I wasn't aware of new capabilities that were previously impossible. > > Ben > > > Le lun. 23 juill. 2018 18 h 06, Ranando King <kingmph at gmail.com <mailto:kingmph at gmail.com>> a écrit : > Granted about `super()`. That's the one thing I can't easily reproduce. However, barring those internal slots, I can reproduce the functionality of `super` and the checks performed as a result of the internal slots, all in ES6. As for built-ins, I can easily and properly extend builtins without `class` since ES6 officially has `Object.setPrototypeOf()`. If you don't think it's possible, you should take a close look at what I'm doing in the repl.it <http://repl.it/> link from my first post. > > As for whether or not the sugary nature of `class` is a good thing, it really is a matter of opinion. I just happen to be of the persuasion that since there's literally no construct that `class` can produce that I cannot reproduce by other means, then that means the `class` keyword (even in light of `super`) is little more than syntactic sugar. As such, we shouldn't be so hasty to turn an Object Oriented Prototype Based language into an Object Oriented Class Based language. The only way to do that reasonably is to ensure that whatever you can construct with `class` can always be equivalently constructed without it. > > Here's a more logical argument instead. Even if there are subtle differences between `class` constructors and object factory functions, providing an isolated path specific to `class` is likely to lead to situations very similar to what happens when an open source package gets forked. Eventually, the difference between the two paths may become so great that one is eventually abandoned (by developers) in favor of the other. This is only a valid argument because the power of ES is in it's simplicity. It's like building a house with wood, nails, sheetrock, etc... (JS) vs. building a house with pre-fabricated parts (class-based languages). > > Don't get me wrong. The `class` keyword is a great thing. It simplifies the production of creating object factories with prototypes. As I understand it, that was the purpose. Let's not make the mistake of allowing something to be done with `class` that cannot be reasonably reproduced without it. The moment we do that, we're diverging from the intended purpose of `class`. > > > > On Mon, Jul 23, 2018 at 4:17 PM Jordan Harband <ljharb at gmail.com <mailto:ljharb at gmail.com>> wrote: > Extend builtins, in particular - ie, `super()` allows your subclass to obtain internal slots it can't otherwise get. > > Even if `class` were just sugar, I don't think I see the argument that that's a *good* thing to preserve. > > On Mon, Jul 23, 2018 at 2:05 PM, Ranando King <kingmph at gmail.com <mailto:kingmph at gmail.com>> wrote: > > > ---------- Forwarded message --------- > From: Ranando King <kingmph at gmail.com <mailto:kingmph at gmail.com>> > Date: Mon, Jul 23, 2018 at 4:04 PM > Subject: Re: proposal: Object Members > To: <ljharb at gmail.com <mailto:ljharb at gmail.com>> > > > You've made that argument before. Exactly what is it in ES6 that you **can** do with `class` that you cannot do without class? I'd like some clarification on this. > > On Mon, Jul 23, 2018 at 3:30 PM Jordan Harband <ljharb at gmail.com <mailto:ljharb at gmail.com>> wrote: > `class` is already not just syntactic sugar, so that notion isn't correct, and shouldn't be maintained. > > On Mon, Jul 23, 2018 at 12:38 PM, Ranando King <kingmph at gmail.com <mailto:kingmph at gmail.com>> wrote: > I've written up a new draft proposal based on my own work with ES5 & ES6 compatible classes with fields. That can be found [here](https://github.com/rdking/proposal-object-members <https://github.com/rdking/proposal-object-members>). I'm already aware of the class-members proposal, but I think it breaks far to many things and doesn't do anything to maintain the notion that "`class` is just syntactic sugar". > > This proposal is specifically based on the code [here](https://github.com/rdking/Class.js/tree/master/es6c <https://github.com/rdking/Class.js/tree/master/es6c>). I've also got a [repl.it <http://repl.it/>](https://repl.it/@arkain/Classjs-Compact-Syntax-ES6 <https://repl.it/@arkain/Classjs-Compact-Syntax-ES6>) that shows the same code running. > > The idea behind the proposal is that instead of injecting a lot of new logic into how `class` works, let's allow `class` to remain syntactic sugar, and put that extra ability into object declarations instead. Then simply allow `class` to do the same with it's own prototypes. > > _______________________________________________ > 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> > > > _______________________________________________ > 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 > https://mail.mozilla.org/listinfo/es-discuss -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180724/872df5d1/attachment-0001.html>
Extend builtins, in particular - ie,
super()
allows your subclass to obtain internal slots it can't otherwise get.Even if
class
were just sugar, I don't think I see the argument that that's a good thing to preserve.
Reflect.construct
allows subclasses to obtain internal slots without
super()
/ class syntax.
const SubDate = function (...args) {
const instance = Reflect.construct(Date, args, SubDate);
return instance;
};
Object.setPrototypeOf(SubDate.prototype, Date.prototype);
const sub = new SubDate();
sub.getDate(); // has internal slots, does not throw
sub instanceof SubDate; // true
sub instanceof Date; // true
This is the first I have heard class
is anything but sugar.
> > Extend builtins, in particular - ie, `super()` allows your subclass to > obtain internal slots it can't otherwise get. > > Even if `class` were just sugar, I don't think I see the argument that > that's a *good* thing to preserve. > `Reflect.construct` allows subclasses to obtain internal slots without `super()` / class syntax. ```js const SubDate = function (...args) { const instance = Reflect.construct(Date, args, SubDate); return instance; }; Object.setPrototypeOf(SubDate.prototype, Date.prototype); const sub = new SubDate(); sub.getDate(); // has internal slots, does not throw sub instanceof SubDate; // true sub instanceof Date; // true ``` This is the first I have heard `class` is anything but sugar. -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180724/45d5e7e7/attachment.html>
On Tue, Jul 24, 2018 at 8:00 AM, Michael Theriot <michael.lee.theriot at gmail.com> wrote:
Reflect.construct
allows subclasses to obtain internal slots withoutsuper()
/ class syntax.
Indeed, Darien pointed that out as well (and if you two hadn't, I would have. :-)
This is the first I have heard
class
is anything but sugar.
The accurate statement would be that class
lets you do things you
couldn't do in ES5. But so does Reflect.construct
. I believe it was
important to the "no new
" crowd that a non-class
mechanism existed for
creating objects using Error and Array as prototypes.
-- T.J. Crowder
On Tue, Jul 24, 2018 at 8:00 AM, Michael Theriot <michael.lee.theriot at gmail.com> wrote: > `Reflect.construct` allows subclasses to obtain internal slots without > `super()` / class syntax. Indeed, Darien pointed that out as well (and if you two hadn't, I would have. :-) > This is the first I have heard `class` is anything but sugar. The accurate statement would be that `class` lets you do things you couldn't do in ES5. But so does `Reflect.construct`. I believe it was important to the "no `new`" crowd that a non-`class` mechanism existed for creating objects using Error and Array as prototypes. -- T.J. Crowder -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180724/e01f5a84/attachment.html>
As you've all pointed out, it's not "just sugar" in the sense that you
couldn't do it in ES5; it's more that parallel syntax and API were created
for the new functionality in ES6. Thanks for providing clear code examples
of how one might extend builtins without class
.
@kai: yes, extending builtins makes sense, in that it's an important part of ES6. Invoking "the web" doesn't negate any of the features of the language, new or old. Separately, not every web use involves any JSON serialization in either direction.
As you've all pointed out, it's not "just sugar" in the sense that you couldn't do it in ES5; it's more that parallel syntax and API were created for the new functionality in ES6. Thanks for providing clear code examples of how one might extend builtins without `class`. @kai: yes, extending builtins makes sense, in that it's an important part of ES6. Invoking "the web" doesn't negate *any* of the features of the language, new or old. Separately, not every web use involves any JSON serialization in either direction. On Tue, Jul 24, 2018 at 12:15 AM, T.J. Crowder < tj.crowder at farsightsoftware.com> wrote: > On Tue, Jul 24, 2018 at 8:00 AM, Michael Theriot > <michael.lee.theriot at gmail.com> wrote: > > `Reflect.construct` allows subclasses to obtain internal slots without > > `super()` / class syntax. > > Indeed, Darien pointed that out as well (and if you two hadn't, I would > have. :-) > > > This is the first I have heard `class` is anything but sugar. > > The accurate statement would be that `class` lets you do things you > couldn't do in ES5. But so does `Reflect.construct`. I believe it was > important to the "no `new`" crowd that a non-`class` mechanism existed for > creating objects using Error and Array as prototypes. > > -- T.J. Crowder > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180724/bd46a939/attachment-0001.html>
@ljharb: It seems you now understand what I was trying to say. Sadly, I'm not always the most eloquent.
As you've all pointed out, it's not "just sugar" in the sense that you
couldn't do it in ES5; it's more that parallel syntax and API were created for the new functionality in ES6.
The intent of my proposal is to provide both member fields and privilege
levels to the class
keyword, and the equivalent for object literals in a
way that meets with both an intuitive declaration style, and a reasonable
access notation that breaks as little as few as possible of the developers
expectations of what can and can't be done.
@ljharb: It seems you now understand what I was trying to say. Sadly, I'm not always the most eloquent. > As you've all pointed out, it's not "just sugar" in the sense that you couldn't do it in ES5; it's more that parallel syntax and API were created for the new functionality in ES6. The intent of my proposal is to provide both member fields and privilege levels to the `class` keyword, and the equivalent for object literals in a way that meets with both an intuitive declaration style, and a reasonable access notation that breaks as little as few as possible of the developers expectations of what can and can't be done. On Tue, Jul 24, 2018 at 3:18 AM Jordan Harband <ljharb at gmail.com> wrote: > As you've all pointed out, it's not "just sugar" in the sense that you > couldn't do it in ES5; it's more that parallel syntax and API were created > for the new functionality in ES6. Thanks for providing clear code examples > of how one might extend builtins without `class`. > > @kai: yes, extending builtins makes sense, in that it's an important part > of ES6. Invoking "the web" doesn't negate *any* of the features of the > language, new or old. Separately, not every web use involves any JSON > serialization in either direction. > > On Tue, Jul 24, 2018 at 12:15 AM, T.J. Crowder < > tj.crowder at farsightsoftware.com> wrote: > >> On Tue, Jul 24, 2018 at 8:00 AM, Michael Theriot >> <michael.lee.theriot at gmail.com> wrote: >> > `Reflect.construct` allows subclasses to obtain internal slots without >> > `super()` / class syntax. >> >> Indeed, Darien pointed that out as well (and if you two hadn't, I would >> have. :-) >> >> > This is the first I have heard `class` is anything but sugar. >> >> The accurate statement would be that `class` lets you do things you >> couldn't do in ES5. But so does `Reflect.construct`. I believe it was >> important to the "no `new`" crowd that a non-`class` mechanism existed for >> creating objects using Error and Array as prototypes. >> >> -- T.J. Crowder >> > > _______________________________________________ > 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/20180724/6cb474ae/attachment.html>
Private fields also won't work as expected and the mandatory super call in constructor is also different from ES5. Let's add species and special class related Symbol so that it makes no sense to define classes "just sugar" + there's no point in avoiding classes at all costs when any of these features is needed.
P.S. Babel mistakenly sold classes as "just sugar" and never worked properly with Custom Elements and builtins extend until version 7 which is still not perfect but at least it doesn't throw errors for no reason
Private fields also won't work as expected and the mandatory super call in constructor is also different from ES5. Let's add species and special class related Symbol so that it makes no sense to define classes "just sugar" + there's no point in avoiding classes at all costs when any of these features is needed. Regards P.S. Babel mistakenly sold classes as "just sugar" and never worked properly with Custom Elements and builtins extend until version 7 which is still not perfect but at least it doesn't throw errors for no reason On Tue, Jul 24, 2018 at 4:15 PM Ranando King <kingmph at gmail.com> wrote: > @ljharb: It seems you now understand what I was trying to say. Sadly, I'm > not always the most eloquent. > > > As you've all pointed out, it's not "just sugar" in the sense that you > couldn't do it in ES5; it's more that parallel syntax and API were > created for the new functionality in ES6. > > The intent of my proposal is to provide both member fields and privilege > levels to the `class` keyword, and the equivalent for object literals in a > way that meets with both an intuitive declaration style, and a reasonable > access notation that breaks as little as few as possible of the developers > expectations of what can and can't be done. > > On Tue, Jul 24, 2018 at 3:18 AM Jordan Harband <ljharb at gmail.com> wrote: > >> As you've all pointed out, it's not "just sugar" in the sense that you >> couldn't do it in ES5; it's more that parallel syntax and API were created >> for the new functionality in ES6. Thanks for providing clear code examples >> of how one might extend builtins without `class`. >> >> @kai: yes, extending builtins makes sense, in that it's an important part >> of ES6. Invoking "the web" doesn't negate *any* of the features of the >> language, new or old. Separately, not every web use involves any JSON >> serialization in either direction. >> >> On Tue, Jul 24, 2018 at 12:15 AM, T.J. Crowder < >> tj.crowder at farsightsoftware.com> wrote: >> >>> On Tue, Jul 24, 2018 at 8:00 AM, Michael Theriot >>> <michael.lee.theriot at gmail.com> wrote: >>> > `Reflect.construct` allows subclasses to obtain internal slots without >>> > `super()` / class syntax. >>> >>> Indeed, Darien pointed that out as well (and if you two hadn't, I would >>> have. :-) >>> >>> > This is the first I have heard `class` is anything but sugar. >>> >>> The accurate statement would be that `class` lets you do things you >>> couldn't do in ES5. But so does `Reflect.construct`. I believe it was >>> important to the "no `new`" crowd that a non-`class` mechanism existed for >>> creating objects using Error and Array as prototypes. >>> >>> -- T.J. Crowder >>> >> >> _______________________________________________ >> 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/20180724/4416f6fa/attachment-0001.html>
Private fields also won't work as expected...
Can you clarify what you're referring to? I've created a library that essentially implements the functional parts of what I intend with this proposal. Of course the syntax isn't the same, and Proxy was used to create a membrane between the public and private storage, and I can't prevent that proxy from being passed to external functions, but those are ES-specific implementation details and not how it would be implemented in the engine.
... the mandatory super call in constructor is also different from ES5.
You shouldn't really try to compare ES5 and ES6. My statement that "class
is syntactic sugar" refers to the fact that anything you can do with
class
in ES6 can also be done without class
in ES6.
P.S. Babel mistakenly sold classes as "just sugar" and never worked
properly with Custom Elements and builtins extend until version 7 which is still not perfect but at least it doesn't throw errors for no reason.
Just because class
is essentially syntactic sugar doesn't mean that the
desugaring is backwards compatible with older versions of the language. I
do not wish to imply that. Nor do I see the need to make such a statement
true. Such an attempt to enforce backwards compatibility to that degree
would prove excessively burdensome on the process of improving and adding
features to the language.
> Private fields also won't work as expected... Can you clarify what you're referring to? I've created a library that essentially implements the functional parts of what I intend with this proposal. Of course the syntax isn't the same, and Proxy was used to create a membrane between the public and private storage, and I can't prevent that proxy from being passed to external functions, but those are ES-specific implementation details and not how it would be implemented in the engine. > ... the mandatory super call in constructor is also different from ES5. You shouldn't really try to compare ES5 and ES6. My statement that "`class` is syntactic sugar" refers to the fact that anything you can do with `class` in ES6 can also be done without `class` in ES6. > P.S. Babel mistakenly sold classes as "just sugar" and never worked properly with Custom Elements and builtins extend until version 7 which is still not perfect but at least it doesn't throw errors for no reason. Just because `class` is essentially syntactic sugar doesn't mean that the desugaring is backwards compatible with older versions of the language. I do not wish to imply that. Nor do I see the need to make such a statement true. Such an attempt to enforce backwards compatibility to that degree would prove excessively burdensome on the process of improving and adding features to the language. On Tue, Jul 24, 2018 at 9:41 AM Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote: > Private fields also won't work as expected and the mandatory super call in > constructor is also different from ES5. Let's add species and special class > related Symbol so that it makes no sense to define classes "just sugar" + > there's no point in avoiding classes at all costs when any of these > features is needed. > > Regards > > P.S. Babel mistakenly sold classes as "just sugar" and never worked > properly with Custom Elements and builtins extend until version 7 which is > still not perfect but at least it doesn't throw errors for no reason > > > On Tue, Jul 24, 2018 at 4:15 PM Ranando King <kingmph at gmail.com> wrote: > >> @ljharb: It seems you now understand what I was trying to say. Sadly, I'm >> not always the most eloquent. >> >> > As you've all pointed out, it's not "just sugar" in the sense that >> you couldn't do it in ES5; it's more that parallel syntax and API were >> created for the new functionality in ES6. >> >> The intent of my proposal is to provide both member fields and privilege >> levels to the `class` keyword, and the equivalent for object literals in a >> way that meets with both an intuitive declaration style, and a reasonable >> access notation that breaks as little as few as possible of the developers >> expectations of what can and can't be done. >> >> On Tue, Jul 24, 2018 at 3:18 AM Jordan Harband <ljharb at gmail.com> wrote: >> >>> As you've all pointed out, it's not "just sugar" in the sense that you >>> couldn't do it in ES5; it's more that parallel syntax and API were created >>> for the new functionality in ES6. Thanks for providing clear code examples >>> of how one might extend builtins without `class`. >>> >>> @kai: yes, extending builtins makes sense, in that it's an important >>> part of ES6. Invoking "the web" doesn't negate *any* of the features of the >>> language, new or old. Separately, not every web use involves any JSON >>> serialization in either direction. >>> >>> On Tue, Jul 24, 2018 at 12:15 AM, T.J. Crowder < >>> tj.crowder at farsightsoftware.com> wrote: >>> >>>> On Tue, Jul 24, 2018 at 8:00 AM, Michael Theriot >>>> <michael.lee.theriot at gmail.com> wrote: >>>> > `Reflect.construct` allows subclasses to obtain internal slots without >>>> > `super()` / class syntax. >>>> >>>> Indeed, Darien pointed that out as well (and if you two hadn't, I would >>>> have. :-) >>>> >>>> > This is the first I have heard `class` is anything but sugar. >>>> >>>> The accurate statement would be that `class` lets you do things you >>>> couldn't do in ES5. But so does `Reflect.construct`. I believe it was >>>> important to the "no `new`" crowd that a non-`class` mechanism existed for >>>> creating objects using Error and Array as prototypes. >>>> >>>> -- T.J. Crowder >>>> >>> >>> _______________________________________________ >>> 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/20180724/2f374446/attachment.html>
Proxy was used to create a membrane between the public and private
storage, and I can't prevent that proxy from being passed to external functions
this already underlines what I mean: classes are not just syntactic sugar because you cannot replicate what they do, not even using ES6.
having privates / proxies maybe exposed is not how I'd personally code.
> Proxy was used to create a membrane between the public and private storage, and I can't prevent that proxy from being passed to external functions this already underlines what I mean: classes are not just syntactic sugar because you cannot replicate what they do, not even using ES6. having privates / proxies maybe exposed is not how I'd personally code. On Tue, Jul 24, 2018 at 5:15 PM Ranando King <kingmph at gmail.com> wrote: > > Private fields also won't work as expected... > > Can you clarify what you're referring to? I've created a library that > essentially implements the functional parts of what I intend with this > proposal. Of course the syntax isn't the same, and Proxy was used to create > a membrane between the public and private storage, and I can't prevent that > proxy from being passed to external functions, but those are ES-specific > implementation details and not how it would be implemented in the engine. > > > ... the mandatory super call in constructor is also different from ES5. > > You shouldn't really try to compare ES5 and ES6. My statement that > "`class` is syntactic sugar" refers to the fact that anything you can do > with `class` in ES6 can also be done without `class` in ES6. > > > P.S. Babel mistakenly sold classes as "just sugar" and never worked > properly with Custom Elements and builtins extend until version 7 which is > still not perfect but at least it doesn't throw errors for no reason. > > Just because `class` is essentially syntactic sugar doesn't mean that the > desugaring is backwards compatible with older versions of the language. I > do not wish to imply that. Nor do I see the need to make such a statement > true. Such an attempt to enforce backwards compatibility to that degree > would prove excessively burdensome on the process of improving and adding > features to the language. > > On Tue, Jul 24, 2018 at 9:41 AM Andrea Giammarchi < > andrea.giammarchi at gmail.com> wrote: > >> Private fields also won't work as expected and the mandatory super call >> in constructor is also different from ES5. Let's add species and special >> class related Symbol so that it makes no sense to define classes "just >> sugar" + there's no point in avoiding classes at all costs when any of >> these features is needed. >> >> Regards >> >> P.S. Babel mistakenly sold classes as "just sugar" and never worked >> properly with Custom Elements and builtins extend until version 7 which is >> still not perfect but at least it doesn't throw errors for no reason >> >> >> On Tue, Jul 24, 2018 at 4:15 PM Ranando King <kingmph at gmail.com> wrote: >> >>> @ljharb: It seems you now understand what I was trying to say. Sadly, >>> I'm not always the most eloquent. >>> >>> > As you've all pointed out, it's not "just sugar" in the sense that >>> you couldn't do it in ES5; it's more that parallel syntax and API were >>> created for the new functionality in ES6. >>> >>> The intent of my proposal is to provide both member fields and privilege >>> levels to the `class` keyword, and the equivalent for object literals in a >>> way that meets with both an intuitive declaration style, and a reasonable >>> access notation that breaks as little as few as possible of the developers >>> expectations of what can and can't be done. >>> >>> On Tue, Jul 24, 2018 at 3:18 AM Jordan Harband <ljharb at gmail.com> wrote: >>> >>>> As you've all pointed out, it's not "just sugar" in the sense that you >>>> couldn't do it in ES5; it's more that parallel syntax and API were created >>>> for the new functionality in ES6. Thanks for providing clear code examples >>>> of how one might extend builtins without `class`. >>>> >>>> @kai: yes, extending builtins makes sense, in that it's an important >>>> part of ES6. Invoking "the web" doesn't negate *any* of the features of the >>>> language, new or old. Separately, not every web use involves any JSON >>>> serialization in either direction. >>>> >>>> On Tue, Jul 24, 2018 at 12:15 AM, T.J. Crowder < >>>> tj.crowder at farsightsoftware.com> wrote: >>>> >>>>> On Tue, Jul 24, 2018 at 8:00 AM, Michael Theriot >>>>> <michael.lee.theriot at gmail.com> wrote: >>>>> > `Reflect.construct` allows subclasses to obtain internal slots >>>>> without >>>>> > `super()` / class syntax. >>>>> >>>>> Indeed, Darien pointed that out as well (and if you two hadn't, I >>>>> would have. :-) >>>>> >>>>> > This is the first I have heard `class` is anything but sugar. >>>>> >>>>> The accurate statement would be that `class` lets you do things you >>>>> couldn't do in ES5. But so does `Reflect.construct`. I believe it was >>>>> important to the "no `new`" crowd that a non-`class` mechanism existed for >>>>> creating objects using Error and Array as prototypes. >>>>> >>>>> -- T.J. Crowder >>>>> >>>> >>>> _______________________________________________ >>>> 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/20180724/2785f121/attachment.html>
What can classes do that ES6 can't?
What can classes do that ES6 can't? On Tuesday, July 24, 2018, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote: > > Proxy was used to create a membrane between the public and private > storage, and I can't prevent that proxy from being passed to external > functions > > this already underlines what I mean: classes are not just syntactic sugar > because you cannot replicate what they do, not even using ES6. > > having privates / proxies maybe exposed is not how I'd personally code. > > > > On Tue, Jul 24, 2018 at 5:15 PM Ranando King <kingmph at gmail.com> wrote: > >> > Private fields also won't work as expected... >> >> Can you clarify what you're referring to? I've created a library that >> essentially implements the functional parts of what I intend with this >> proposal. Of course the syntax isn't the same, and Proxy was used to create >> a membrane between the public and private storage, and I can't prevent that >> proxy from being passed to external functions, but those are ES-specific >> implementation details and not how it would be implemented in the engine. >> >> > ... the mandatory super call in constructor is also different from >> ES5. >> >> You shouldn't really try to compare ES5 and ES6. My statement that >> "`class` is syntactic sugar" refers to the fact that anything you can do >> with `class` in ES6 can also be done without `class` in ES6. >> >> > P.S. Babel mistakenly sold classes as "just sugar" and never worked >> properly with Custom Elements and builtins extend until version 7 which is >> still not perfect but at least it doesn't throw errors for no reason. >> >> Just because `class` is essentially syntactic sugar doesn't mean that the >> desugaring is backwards compatible with older versions of the language. I >> do not wish to imply that. Nor do I see the need to make such a statement >> true. Such an attempt to enforce backwards compatibility to that degree >> would prove excessively burdensome on the process of improving and adding >> features to the language. >> >> On Tue, Jul 24, 2018 at 9:41 AM Andrea Giammarchi < >> andrea.giammarchi at gmail.com> wrote: >> >>> Private fields also won't work as expected and the mandatory super call >>> in constructor is also different from ES5. Let's add species and special >>> class related Symbol so that it makes no sense to define classes "just >>> sugar" + there's no point in avoiding classes at all costs when any of >>> these features is needed. >>> >>> Regards >>> >>> P.S. Babel mistakenly sold classes as "just sugar" and never worked >>> properly with Custom Elements and builtins extend until version 7 which is >>> still not perfect but at least it doesn't throw errors for no reason >>> >>> >>> On Tue, Jul 24, 2018 at 4:15 PM Ranando King <kingmph at gmail.com> wrote: >>> >>>> @ljharb: It seems you now understand what I was trying to say. Sadly, >>>> I'm not always the most eloquent. >>>> >>>> > As you've all pointed out, it's not "just sugar" in the sense that >>>> you couldn't do it in ES5; it's more that parallel syntax and API were >>>> created for the new functionality in ES6. >>>> >>>> The intent of my proposal is to provide both member fields and >>>> privilege levels to the `class` keyword, and the equivalent for object >>>> literals in a way that meets with both an intuitive declaration style, and >>>> a reasonable access notation that breaks as little as few as possible of >>>> the developers expectations of what can and can't be done. >>>> >>>> On Tue, Jul 24, 2018 at 3:18 AM Jordan Harband <ljharb at gmail.com> >>>> wrote: >>>> >>>>> As you've all pointed out, it's not "just sugar" in the sense that you >>>>> couldn't do it in ES5; it's more that parallel syntax and API were created >>>>> for the new functionality in ES6. Thanks for providing clear code examples >>>>> of how one might extend builtins without `class`. >>>>> >>>>> @kai: yes, extending builtins makes sense, in that it's an important >>>>> part of ES6. Invoking "the web" doesn't negate *any* of the features of the >>>>> language, new or old. Separately, not every web use involves any JSON >>>>> serialization in either direction. >>>>> >>>>> On Tue, Jul 24, 2018 at 12:15 AM, T.J. Crowder < >>>>> tj.crowder at farsightsoftware.com> wrote: >>>>> >>>>>> On Tue, Jul 24, 2018 at 8:00 AM, Michael Theriot >>>>>> <michael.lee.theriot at gmail.com> wrote: >>>>>> > `Reflect.construct` allows subclasses to obtain internal slots >>>>>> without >>>>>> > `super()` / class syntax. >>>>>> >>>>>> Indeed, Darien pointed that out as well (and if you two hadn't, I >>>>>> would have. :-) >>>>>> >>>>>> > This is the first I have heard `class` is anything but sugar. >>>>>> >>>>>> The accurate statement would be that `class` lets you do things you >>>>>> couldn't do in ES5. But so does `Reflect.construct`. I believe it was >>>>>> important to the "no `new`" crowd that a non-`class` mechanism existed for >>>>>> creating objects using Error and Array as prototypes. >>>>>> >>>>>> -- T.J. Crowder >>>>>> >>>>> >>>>> _______________________________________________ >>>>> 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/20180724/4d2c9878/attachment-0001.html>
this already underlines what I mean: classes are not just syntactic sugar
because you cannot replicate what they do, not even using ES6.
You're misunderstanding a few things.
- Any ES6 class I create using the
class
keyword, I can recreate without theclass
keyword. Period. There's no exception to this. - What I was describing to you was the work I put in to create a
class
factory function that allows developers to easily createclass
instances with data fields of varying privilege. This work is the conceptual basis of my proposal. I never claimed even once that my example code was somehow a flawless implementation of my proposal. That would be expressly impossible. If it weren't, then I wouldn't be asking for a new feature. The fact that in ES6 I cannot prevent a developer from passing the proxied context object to an external method or assign it to an external variable is not an issue that would be suffered by an in engine implementation of my proposal. However, that would be a different version of ES, not ES6.
> this already underlines what I mean: classes are not just syntactic sugar because you cannot replicate what they do, not even using ES6. You're misunderstanding a few things. 1. Any ES6 class I create using the `class` keyword, I can recreate without the `class` keyword. Period. There's no exception to this. 2. What I was describing to you was the work I put in to create a `class` factory function that allows developers to easily create `class` instances with data fields of varying privilege. This work is the **_conceptual basis_** of my proposal. I never claimed even once that my example code was somehow a flawless implementation of my proposal. That would be expressly impossible. If it weren't, then I wouldn't be asking for a new feature. The fact that in ES6 I cannot prevent a developer from passing the proxied context object to an external method or assign it to an external variable is not an issue that would be suffered by an in engine implementation of my proposal. However, that would be a different version of ES, not ES6. On Tue, Jul 24, 2018 at 10:29 AM Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote: > > Proxy was used to create a membrane between the public and private > storage, and I can't prevent that proxy from being passed to external > functions > > this already underlines what I mean: classes are not just syntactic sugar > because you cannot replicate what they do, not even using ES6. > > having privates / proxies maybe exposed is not how I'd personally code. > > > > On Tue, Jul 24, 2018 at 5:15 PM Ranando King <kingmph at gmail.com> wrote: > >> > Private fields also won't work as expected... >> >> Can you clarify what you're referring to? I've created a library that >> essentially implements the functional parts of what I intend with this >> proposal. Of course the syntax isn't the same, and Proxy was used to create >> a membrane between the public and private storage, and I can't prevent that >> proxy from being passed to external functions, but those are ES-specific >> implementation details and not how it would be implemented in the engine. >> >> > ... the mandatory super call in constructor is also different from >> ES5. >> >> You shouldn't really try to compare ES5 and ES6. My statement that >> "`class` is syntactic sugar" refers to the fact that anything you can do >> with `class` in ES6 can also be done without `class` in ES6. >> >> > P.S. Babel mistakenly sold classes as "just sugar" and never worked >> properly with Custom Elements and builtins extend until version 7 which is >> still not perfect but at least it doesn't throw errors for no reason. >> >> Just because `class` is essentially syntactic sugar doesn't mean that the >> desugaring is backwards compatible with older versions of the language. I >> do not wish to imply that. Nor do I see the need to make such a statement >> true. Such an attempt to enforce backwards compatibility to that degree >> would prove excessively burdensome on the process of improving and adding >> features to the language. >> >> On Tue, Jul 24, 2018 at 9:41 AM Andrea Giammarchi < >> andrea.giammarchi at gmail.com> wrote: >> >>> Private fields also won't work as expected and the mandatory super call >>> in constructor is also different from ES5. Let's add species and special >>> class related Symbol so that it makes no sense to define classes "just >>> sugar" + there's no point in avoiding classes at all costs when any of >>> these features is needed. >>> >>> Regards >>> >>> P.S. Babel mistakenly sold classes as "just sugar" and never worked >>> properly with Custom Elements and builtins extend until version 7 which is >>> still not perfect but at least it doesn't throw errors for no reason >>> >>> >>> On Tue, Jul 24, 2018 at 4:15 PM Ranando King <kingmph at gmail.com> wrote: >>> >>>> @ljharb: It seems you now understand what I was trying to say. Sadly, >>>> I'm not always the most eloquent. >>>> >>>> > As you've all pointed out, it's not "just sugar" in the sense that >>>> you couldn't do it in ES5; it's more that parallel syntax and API were >>>> created for the new functionality in ES6. >>>> >>>> The intent of my proposal is to provide both member fields and >>>> privilege levels to the `class` keyword, and the equivalent for object >>>> literals in a way that meets with both an intuitive declaration style, and >>>> a reasonable access notation that breaks as little as few as possible of >>>> the developers expectations of what can and can't be done. >>>> >>>> On Tue, Jul 24, 2018 at 3:18 AM Jordan Harband <ljharb at gmail.com> >>>> wrote: >>>> >>>>> As you've all pointed out, it's not "just sugar" in the sense that you >>>>> couldn't do it in ES5; it's more that parallel syntax and API were created >>>>> for the new functionality in ES6. Thanks for providing clear code examples >>>>> of how one might extend builtins without `class`. >>>>> >>>>> @kai: yes, extending builtins makes sense, in that it's an important >>>>> part of ES6. Invoking "the web" doesn't negate *any* of the features of the >>>>> language, new or old. Separately, not every web use involves any JSON >>>>> serialization in either direction. >>>>> >>>>> On Tue, Jul 24, 2018 at 12:15 AM, T.J. Crowder < >>>>> tj.crowder at farsightsoftware.com> wrote: >>>>> >>>>>> On Tue, Jul 24, 2018 at 8:00 AM, Michael Theriot >>>>>> <michael.lee.theriot at gmail.com> wrote: >>>>>> > `Reflect.construct` allows subclasses to obtain internal slots >>>>>> without >>>>>> > `super()` / class syntax. >>>>>> >>>>>> Indeed, Darien pointed that out as well (and if you two hadn't, I >>>>>> would have. :-) >>>>>> >>>>>> > This is the first I have heard `class` is anything but sugar. >>>>>> >>>>>> The accurate statement would be that `class` lets you do things you >>>>>> couldn't do in ES5. But so does `Reflect.construct`. I believe it was >>>>>> important to the "no `new`" crowd that a non-`class` mechanism existed for >>>>>> creating objects using Error and Array as prototypes. >>>>>> >>>>>> -- T.J. Crowder >>>>>> >>>>> >>>>> _______________________________________________ >>>>> 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/20180724/0b8e96e9/attachment.html>
Since it seems to be such an inflammatory statement, I have removed most of
the comments that infer the sugar-like nature of class
from my proposal.
It counterproductive to get stuck on whether or not class
is syntactic
sugar when the point is supposed to be about how to extend the language
features around the class
keyword.
Since it seems to be such an inflammatory statement, I have removed most of the comments that infer the sugar-like nature of `class` from my proposal. It counterproductive to get stuck on whether or not `class` is syntactic sugar when the point is supposed to be about how to extend the language features around the `class` keyword. On Tue, Jul 24, 2018 at 9:14 AM Ranando King <kingmph at gmail.com> wrote: > @ljharb: It seems you now understand what I was trying to say. Sadly, I'm > not always the most eloquent. > > > As you've all pointed out, it's not "just sugar" in the sense that you > couldn't do it in ES5; it's more that parallel syntax and API were > created for the new functionality in ES6. > > The intent of my proposal is to provide both member fields and privilege > levels to the `class` keyword, and the equivalent for object literals in a > way that meets with both an intuitive declaration style, and a reasonable > access notation that breaks as little as few as possible of the developers > expectations of what can and can't be done. > > On Tue, Jul 24, 2018 at 3:18 AM Jordan Harband <ljharb at gmail.com> wrote: > >> As you've all pointed out, it's not "just sugar" in the sense that you >> couldn't do it in ES5; it's more that parallel syntax and API were created >> for the new functionality in ES6. Thanks for providing clear code examples >> of how one might extend builtins without `class`. >> >> @kai: yes, extending builtins makes sense, in that it's an important part >> of ES6. Invoking "the web" doesn't negate *any* of the features of the >> language, new or old. Separately, not every web use involves any JSON >> serialization in either direction. >> >> On Tue, Jul 24, 2018 at 12:15 AM, T.J. Crowder < >> tj.crowder at farsightsoftware.com> wrote: >> >>> On Tue, Jul 24, 2018 at 8:00 AM, Michael Theriot >>> <michael.lee.theriot at gmail.com> wrote: >>> > `Reflect.construct` allows subclasses to obtain internal slots without >>> > `super()` / class syntax. >>> >>> Indeed, Darien pointed that out as well (and if you two hadn't, I would >>> have. :-) >>> >>> > This is the first I have heard `class` is anything but sugar. >>> >>> The accurate statement would be that `class` lets you do things you >>> couldn't do in ES5. But so does `Reflect.construct`. I believe it was >>> important to the "no `new`" crowd that a non-`class` mechanism existed for >>> creating objects using Error and Array as prototypes. >>> >>> -- T.J. Crowder >>> >> >> _______________________________________________ >> 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/20180724/47cd9648/attachment-0001.html>
On Mon, Jul 23, 2018 at 8:38 PM, Ranando King <kingmph at gmail.com> wrote:
I've written up a new draft proposal based on my own work with ES5 & ES6 compatible classes with fields. That can be found here. I'm already
aware
of the class-members proposal, but I think it breaks far to many things...
That's quite vague.
Am I misunderstanding your intent here? It seems like you're proposing dropping the existing Stage 3 proposals (1, 2) in favor of this new proposal. Those proposals are the result of years of work and collaboration. It seems like any issues you have with them would be better addressed by raising issues on those proposals and engaging with the people working on them, rather than suggesting just throwing them out entirely and replacing them with something new and different. But perhaps I'm misunderstanding your intent?
-- T.J. Crowder
On Mon, Jul 23, 2018 at 8:38 PM, Ranando King <kingmph at gmail.com> wrote: > I've written up a new draft proposal based on my own work with ES5 & ES6 > compatible classes with fields. That can be found > [here](https://github.com/rdking/proposal-object-members). I'm already aware > of the class-members proposal, but I think it breaks far to many things... That's quite vague. Am I misunderstanding your intent here? It seems like you're proposing dropping the existing Stage 3 proposals ([1][1], [2][2]) in favor of this new proposal. Those proposals are the result of years of work and collaboration. It seems like any issues you have with them would be better addressed by raising issues on those proposals and engaging with the people working on them, rather than suggesting just throwing them out entirely and replacing them with something new and different. But perhaps I'm misunderstanding your intent? -- T.J. Crowder [1]: https://github.com/tc39/proposal-class-fields [2]: https://github.com/tc39/proposal-static-class-features/ -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180725/ddd91170/attachment.html>
Let me see if I can clarify things a bit:
You're not misunderstanding. I've spent a fair amount of time in discussion on github in those very same issues. For various reasons, probably mostly due to the time investment the proponents of those proposals have given, it seems to be the prevailing view that the proposal as it stands, not withstanding the many glaring issues its detractors keep raising, is the best possible proposal that can be given. What I'm doing with my proposal is raising a formal counter-argument. I'm not simply developing a new proposal adhoc. Instead, I'm taking advantage of the many points and issues raised about the existing proposals and integrating the best possible solutions I and others can come up with that haven't been shot down for any logical or rational reason into a single new, self-coherent, non-binding, and mostly "means what you'd expect at first glance" proposal.
My goals are straight forward. I'll list them:
- Introduce the concept of non-public privilege levels (
private
&protected
) - Introduce a self-consistent means of accessing non-public members
(operator
#
) - Introduce a means of declaring non-public members with and without
the
class
keyword - Prevent the introduction of special case characters in an
[[IdentifierName]]
- Prevent the breaking of the
obj.field === obj['field']
usage pattern - Prevent the unnecessary isolation of functionality into the
class
keyword - Prevent the unnecessary limiting of future language expansion due to excessively limiting new feature.
Truth be told, the proposal I'm offering can be thought of as a heavy handed revision of the two proposals above. I am presently adding to my proposal a description of the implementation details required to make it all work. I hope that at the very least, from this you and others will be able to see that there is indeed a less limiting possibility than what has been proposed by proposal-class-fields. Even though I am submitting my own proposal, I will still continue to add my observations to the existing proposals.
Let me see if I can clarify things a bit: You're not misunderstanding. I've spent a fair amount of time in discussion on github in those very same issues. For various reasons, probably mostly due to the time investment the proponents of those proposals have given, it seems to be the prevailing view that the proposal as it stands, not withstanding the many glaring issues its detractors keep raising, is the best possible proposal that can be given. What I'm doing with my proposal is raising a formal counter-argument. I'm not simply developing a new proposal adhoc. Instead, I'm taking advantage of the many points and issues raised about the existing proposals and integrating the best possible solutions I and others can come up with that haven't been shot down for any logical or rational reason into a single new, self-coherent, non-binding, and mostly "means what you'd expect at first glance" proposal. My goals are straight forward. I'll list them: 1. Introduce the concept of non-public privilege levels (`private` & `protected`) 2. Introduce a self-consistent means of accessing non-public members (operator `#`) 3. Introduce a means of declaring non-public members **with and without** the `class` keyword 4. Prevent the introduction of special case characters in an `[[IdentifierName]]` 5. Prevent the breaking of the `obj.field === obj['field']` usage pattern 6. Prevent the unnecessary isolation of functionality into the `class` keyword 7. Prevent the unnecessary limiting of future language expansion due to excessively limiting new feature. Truth be told, the proposal I'm offering can be thought of as a heavy handed revision of the two proposals above. I am presently adding to my proposal a description of the implementation details required to make it all work. I hope that at the very least, from this you and others will be able to see that there is indeed a less limiting possibility than what has been proposed by proposal-class-fields. Even though I am submitting my own proposal, I will still continue to add my observations to the existing proposals. On Wed, Jul 25, 2018 at 2:23 AM T.J. Crowder < tj.crowder at farsightsoftware.com> wrote: > On Mon, Jul 23, 2018 at 8:38 PM, Ranando King > <kingmph at gmail.com> wrote: > > I've written up a new draft proposal based on my own work with ES5 & ES6 > > compatible classes with fields. That can be found > > [here](https://github.com/rdking/proposal-object-members). I'm already > aware > > of the class-members proposal, but I think it breaks far to many > things... > > That's quite vague. > > Am I misunderstanding your intent here? It seems like you're proposing > dropping the existing Stage 3 proposals ([1][1], [2][2]) in favor of this > new proposal. Those proposals are the result of years of work and > collaboration. It seems like any issues you have with them would be better > addressed by raising issues on those proposals and engaging with the people > working on them, rather than suggesting just throwing them out entirely and > replacing them with something new and different. But perhaps I'm > misunderstanding your intent? > > -- T.J. Crowder > > [1]: https://github.com/tc39/proposal-class-fields > [2]: https://github.com/tc39/proposal-static-class-features/ > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180726/bcd6ce76/attachment.html>
On Thu, Jul 26, 2018 at 7:28 AM, Ranando King <kingmph at gmail.com> wrote:
Let me see if I can clarify things a bit:
Thanks! That does indeed clear things up a lot.
I'd find it helpful (just speaking for myself here) if the proposal opened with a series of specific differences with the two existing proposals and statements of why your way is better. (If it refers to existing issues raised on their repos, links are great with a summary of where you think the issue stands.) You've made a few statements along those lines in that list, but if you could flesh it out in the proposal, that would be really helpful.
-- T.J. Crowder
On Thu, Jul 26, 2018 at 7:28 AM, Ranando King <kingmph at gmail.com> wrote: > Let me see if I can clarify things a bit: Thanks! That does indeed clear things up a lot. I'd find it helpful (just speaking for myself here) if the proposal opened with a series of specific differences with the two existing proposals and statements of why your way is better. (If it refers to existing issues raised on their repos, links are great with a summary of where you think the issue stands.) You've made a few statements along those lines in that list, but if you could flesh it out in the proposal, that would be really helpful. -- T.J. Crowder -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180726/630cdb93/attachment-0001.html>
Thank you for the advice. Those will be the next updates I make to the proposal.
Thank you for the advice. Those will be the next updates I make to the proposal. On Thu, Jul 26, 2018 at 3:32 AM T.J. Crowder < tj.crowder at farsightsoftware.com> wrote: > On Thu, Jul 26, 2018 at 7:28 AM, Ranando King <kingmph at gmail.com> wrote: > > Let me see if I can clarify things a bit: > > Thanks! That does indeed clear things up a lot. > > I'd find it helpful (just speaking for myself here) if the proposal opened > with a series of specific differences with the two existing proposals and > statements of why your way is better. (If it refers to existing issues > raised on their repos, links are great with a summary of where you think > the issue stands.) You've made a few statements along those lines in that > list, but if you could flesh it out in the proposal, that would be really > helpful. > > -- T.J. Crowder > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180726/df6b0e8e/attachment.html>
I've just finished updating my proposal with an Existing proposals section that lists the major differences.
I've just finished updating my proposal with an [Existing proposals]( https://github.com/rdking/proposal-object-members/blob/master/README.md#existing-proposals) section that lists the major differences. On Thu, Jul 26, 2018 at 9:00 AM Ranando King <kingmph at gmail.com> wrote: > Thank you for the advice. Those will be the next updates I make to the > proposal. > > On Thu, Jul 26, 2018 at 3:32 AM T.J. Crowder < > tj.crowder at farsightsoftware.com> wrote: > >> On Thu, Jul 26, 2018 at 7:28 AM, Ranando King <kingmph at gmail.com> wrote: >> > Let me see if I can clarify things a bit: >> >> Thanks! That does indeed clear things up a lot. >> >> I'd find it helpful (just speaking for myself here) if the proposal >> opened with a series of specific differences with the two existing >> proposals and statements of why your way is better. (If it refers to >> existing issues raised on their repos, links are great with a summary of >> where you think the issue stands.) You've made a few statements along those >> lines in that list, but if you could flesh it out in the proposal, that >> would be really helpful. >> >> -- T.J. Crowder >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180726/042753a3/attachment.html>
On 07/26/2018 01:55 PM, Ranando King wrote:
I've just finished updating my proposal with an Existing proposals section that lists the major differences.
Reading the proposal, I'm not yet sure what it's supposed to do. Some things I've noticed:
-
The proposal commingles static and instance private properties. In the first example, you could read or write this#.field2, and it would work. How would you encode the generic expression a#.[b]?
-
Worse, the proposal commingles all private properties across all classes. There's nothing in the proposed code stopping you from reading and writing private properties defined by class A on instances of an unrelated class B.
Waldemar
On 07/26/2018 01:55 PM, Ranando King wrote: > I've just finished updating my proposal with an [Existing proposals](https://github.com/rdking/proposal-object-members/blob/master/README.md#existing-proposals) section that lists the major differences. Reading the proposal, I'm not yet sure what it's supposed to do. Some things I've noticed: - The proposal commingles static and instance private properties. In the first example, you could read or write this#.field2, and it would work. How would you encode the generic expression a#.[b]? - Worse, the proposal commingles all private properties across all classes. There's nothing in the proposed code stopping you from reading and writing private properties defined by class A on instances of an unrelated class B. Waldemar
The proposal commingles static and instance private properties. In the
first example, you could read or write this#.field2, and it would work. How would you encode the generic expression a#.[b]?
That's not quite right. I added some details about how all this works a few
hours after you wrote this message. Here's the gist:
The static
keyword would cause field2
to be placed in the
[[PrivateValues]]
record of the function. The protected
keyword would
cause a key/value pair resembling "field2": Symbol("field2")
to be placed
in the [[DeclarationInfo]]
record of the function. Since you used this
,
I will assume it is an instance of ExampleFn
. The expression
this#.field2
within ExampleFn
would translate into
`this.[[PrivateValues]][this.[[DeclarationInfo]].field2] ->
this.[[PrivateValues]][undefined] -> TypeError`
assuming that the prototype of ExampleFn
contained no definition for
field2. Since the static
field belongs to the function it is declared in,
it can only be accessed via the function object itself, i.e.
ExampleFn#.field2
or this.constructor#.field2
or the array-notation
equivalents. I can't say that I'm understanding what you're looking for
with a#.[b]
That looks like a syntax error to me. If you're trying to
understand how a#[b]
works, it would be exactly the same. The best way to
understand how it works is to look at this equation for the #
operator.
<lParam>#<rParam> === <lParam>.[[PrivateValues]][<lParam>.[[DeclarationInfo]]<rParam>]
In the case of a#[b]
, the result would be:
a.[[PrivateValues]][a.[[DeclarationInfo]] [b] ]
I get that all the square braces can make that hard to read, so here's the step by step:
- Throw a type error if a is not an object
- Let D be the [[DeclarationInfo]] record of object.
- if D is not in the lexical scope chain of the current function, throw a TypeError
- Let N be the value for the key matching b in D, or undefined if no such key exists.
- Let P be the [[PrivateValues]] record of object a
- If N is not a key of P, Throw a TypeError
- return P[N]
Worse, the proposal commingles all private properties across all
classes. There's nothing in the proposed code stopping you from reading and writing private properties defined by class A on instances of an unrelated class B.
That's not right either. The example desugaring does indeed throw all maps into the same WeakMap. I'll work on that to make a better example. However, to get a better understanding of how I see the implementation, you should read the implementation details section. There's a much better description there. Besides, the 7 step description I just wrote for you should help you realize that:
- If the function you're in doesn't know the names of the private fields of your object, you get a TypeError.
- If the object you're accessing doesn't recognize the name of the field you're trying to access, you get a TypeError.
So there really is no way to apply the private field names of class A
onto an instance of class B
.
I'll spend some time tonight re-tooling my example desugaring and a few other details I thought of while writing this in the proposal. Hopefully, that'll prevent more confusion.
> The proposal commingles static and instance private properties. In the first example, you could read or write this#.field2, and it would work. How would you encode the generic expression a#.[b]? That's not quite right. I added some details about how all this works a few hours after you wrote this message. Here's the gist: The `static` keyword would cause `field2` to be placed in the `[[PrivateValues]]` record of the function. The `protected` keyword would cause a key/value pair resembling `"field2": Symbol("field2")` to be placed in the `[[DeclarationInfo]]` record of the function. Since you used `this`, I will assume it is an instance of `ExampleFn`. The expression `this#.field2` within `ExampleFn` would translate into `this.[[PrivateValues]][this.[[DeclarationInfo]].field2] -> this.[[PrivateValues]][undefined] -> TypeError` assuming that the prototype of `ExampleFn` contained no definition for field2. Since the `static` field belongs to the function it is declared in, it can only be accessed via the function object itself, i.e. `ExampleFn#.field2` or `this.constructor#.field2` or the array-notation equivalents. I can't say that I'm understanding what you're looking for with `a#.[b]` That looks like a syntax error to me. If you're trying to understand how `a#[b]` works, it would be exactly the same. The best way to understand how it works is to look at this equation for the `#` operator. <lParam>#<rParam> === <lParam>.[[PrivateValues]][<lParam>.[[DeclarationInfo]]<rParam>] In the case of `a#[b]`, the result would be: a.[[PrivateValues]][a.[[DeclarationInfo]] [b] ] I get that all the square braces can make that hard to read, so here's the step by step: 1. Throw a type error if a is not an object 2. Let D be the [[DeclarationInfo]] record of object. 3. if D is not in the lexical scope chain of the current function, throw a TypeError 4. Let N be the value for the key matching b in D, or undefined if no such key exists. 5. Let P be the [[PrivateValues]] record of object a 6. If N is not a key of P, Throw a TypeError 7. return P[N] > Worse, the proposal commingles all private properties across all classes. There's nothing in the proposed code stopping you from reading and writing private properties defined by class A on instances of an unrelated class B. That's not right either. The example desugaring does indeed throw all maps into the same WeakMap. I'll work on that to make a better example. However, to get a better understanding of how I see the implementation, you should read the implementation details section. There's a much better description there. Besides, the 7 step description I just wrote for you should help you realize that: - If the function you're in doesn't know the names of the private fields of your object, you get a TypeError. - If the object you're accessing doesn't recognize the name of the field you're trying to access, you get a TypeError. So there really is no way to apply the private field names of `class A` onto an instance of `class B`. I'll spend some time tonight re-tooling my example desugaring and a few other details I thought of while writing this in the proposal. Hopefully, that'll prevent more confusion. On Fri, Jul 27, 2018 at 7:55 PM Waldemar Horwat <waldemar at google.com> wrote: > On 07/26/2018 01:55 PM, Ranando King wrote: > > I've just finished updating my proposal with an [Existing proposals]( > https://github.com/rdking/proposal-object-members/blob/master/README.md#existing-proposals) > section that lists the major differences. > > Reading the proposal, I'm not yet sure what it's supposed to do. Some > things I've noticed: > > - The proposal commingles static and instance private properties. In the > first example, you could read or write this#.field2, and it would work. > How would you encode the generic expression a#.[b]? > > - Worse, the proposal commingles all private properties across all > classes. There's nothing in the proposed code stopping you from reading > and writing private properties defined by class A on instances of an > unrelated class B. > > Waldemar > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180727/f1779f44/attachment.html>
If the "name" is just a string, though, then it's just another global namespace. I could access a private field named "id" on every object that had one, in a try/catch, and i'd be able to not just observe if an object has an "id" private field, but access or alter its contents.
If the "name" is just a string, though, then it's just another global namespace. I could access a private field named "id" on every object that had one, in a try/catch, and i'd be able to not just observe if an object has an "id" private field, but access or alter its contents. On Fri, Jul 27, 2018 at 9:37 PM, Ranando King <kingmph at gmail.com> wrote: > > The proposal commingles static and instance private properties. In the > first example, you could read or write this#.field2, and it would work. > How would you encode the generic expression a#.[b]? > > That's not quite right. I added some details about how all this works a > few hours after you wrote this message. Here's the gist: > The `static` keyword would cause `field2` to be placed in the > `[[PrivateValues]]` record of the function. The `protected` keyword would > cause a key/value pair resembling `"field2": Symbol("field2")` to be placed > in the `[[DeclarationInfo]]` record of the function. Since you used `this`, > I will assume it is an instance of `ExampleFn`. The expression > `this#.field2` within `ExampleFn` would translate into > > `this.[[PrivateValues]][this.[[DeclarationInfo]].field2] -> > this.[[PrivateValues]][undefined] -> TypeError` > > assuming that the prototype of `ExampleFn` contained no definition for > field2. Since the `static` field belongs to the function it is declared in, > it can only be accessed via the function object itself, i.e. > `ExampleFn#.field2` or `this.constructor#.field2` or the array-notation > equivalents. I can't say that I'm understanding what you're looking for > with `a#.[b]` That looks like a syntax error to me. If you're trying to > understand how `a#[b]` works, it would be exactly the same. The best way to > understand how it works is to look at this equation for the `#` operator. > > <lParam>#<rParam> === <lParam>.[[PrivateValues]][< > lParam>.[[DeclarationInfo]]<rParam>] > > In the case of `a#[b]`, the result would be: > > a.[[PrivateValues]][a.[[DeclarationInfo]] [b] ] > > I get that all the square braces can make that hard to read, so here's the > step by step: > > 1. Throw a type error if a is not an object > 2. Let D be the [[DeclarationInfo]] record of object. > 3. if D is not in the lexical scope chain of the current function, > throw a TypeError > 4. Let N be the value for the key matching b in D, or undefined if no > such key exists. > 5. Let P be the [[PrivateValues]] record of object a > 6. If N is not a key of P, Throw a TypeError > 7. return P[N] > > > Worse, the proposal commingles all private properties across all > classes. There's nothing in the proposed code stopping you from reading > and writing private properties defined by class A on instances of an > unrelated class B. > > That's not right either. The example desugaring does indeed throw all maps > into the same WeakMap. I'll work on that to make a better example. However, > to get a better understanding of how I see the implementation, you should > read the implementation details section. There's a much better description > there. Besides, the 7 step description I just wrote for you should help you > realize that: > > - If the function you're in doesn't know the names of the private > fields of your object, you get a TypeError. > - If the object you're accessing doesn't recognize the name of the > field you're trying to access, you get a TypeError. > > So there really is no way to apply the private field names of `class A` > onto an instance of `class B`. > > I'll spend some time tonight re-tooling my example desugaring and a few > other details I thought of while writing this in the proposal. Hopefully, > that'll prevent more confusion. > > > On Fri, Jul 27, 2018 at 7:55 PM Waldemar Horwat <waldemar at google.com> > wrote: > >> On 07/26/2018 01:55 PM, Ranando King wrote: >> > I've just finished updating my proposal with an [Existing proposals]( >> https://github.com/rdking/proposal-object-members/blob/master/README.md# >> existing-proposals) section that lists the major differences. >> >> Reading the proposal, I'm not yet sure what it's supposed to do. Some >> things I've noticed: >> >> - The proposal commingles static and instance private properties. In the >> first example, you could read or write this#.field2, and it would work. >> How would you encode the generic expression a#.[b]? >> >> - Worse, the proposal commingles all private properties across all >> classes. There's nothing in the proposed code stopping you from reading >> and writing private properties defined by class A on instances of an >> unrelated class B. >> >> Waldemar >> > > _______________________________________________ > 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/20180727/bbdaaf1e/attachment-0001.html>
You're almost right about that. The guardian logic that ensures this cannot
happen is that the [[DeclarationInfo]]
of the instance whose private
fields are being accessed must either be or be a prototype of the
[[DeclarationInfo]]
of the current function. This was part of the update
I made to the proposal last night after replying to Waldemar. The only way
for this to be true is that the current function would have been declared
within the same declaration scope as the function used to create the
instance. When this match doesn't occur, use of the #
operator causes a
TypeError.
You're almost right about that. The guardian logic that ensures this cannot happen is that the `[[DeclarationInfo]]` of the instance whose private fields are being accessed must either be or be a prototype of the `[[DeclarationInfo]]` of the current function. This was part of the update I made to the proposal last night after replying to Waldemar. The only way for this to be true is that the current function would have been declared within the same declaration scope as the function used to create the instance. When this match doesn't occur, use of the `#` operator causes a TypeError. On Sat, Jul 28, 2018 at 1:47 AM Jordan Harband <ljharb at gmail.com> wrote: > If the "name" is just a string, though, then it's just another global > namespace. I could access a private field named "id" on every object that > had one, in a try/catch, and i'd be able to not just observe if an object > has an "id" private field, but access or alter its contents. > > On Fri, Jul 27, 2018 at 9:37 PM, Ranando King <kingmph at gmail.com> wrote: > >> > The proposal commingles static and instance private properties. In >> the first example, you could read or write this#.field2, and it would >> work. How would you encode the generic expression a#.[b]? >> >> That's not quite right. I added some details about how all this works a >> few hours after you wrote this message. Here's the gist: >> The `static` keyword would cause `field2` to be placed in the >> `[[PrivateValues]]` record of the function. The `protected` keyword would >> cause a key/value pair resembling `"field2": Symbol("field2")` to be placed >> in the `[[DeclarationInfo]]` record of the function. Since you used `this`, >> I will assume it is an instance of `ExampleFn`. The expression >> `this#.field2` within `ExampleFn` would translate into >> >> `this.[[PrivateValues]][this.[[DeclarationInfo]].field2] -> >> this.[[PrivateValues]][undefined] -> TypeError` >> >> assuming that the prototype of `ExampleFn` contained no definition for >> field2. Since the `static` field belongs to the function it is declared in, >> it can only be accessed via the function object itself, i.e. >> `ExampleFn#.field2` or `this.constructor#.field2` or the array-notation >> equivalents. I can't say that I'm understanding what you're looking for >> with `a#.[b]` That looks like a syntax error to me. If you're trying to >> understand how `a#[b]` works, it would be exactly the same. The best way to >> understand how it works is to look at this equation for the `#` operator. >> >> <lParam>#<rParam> === >> <lParam>.[[PrivateValues]][<lParam>.[[DeclarationInfo]]<rParam>] >> >> In the case of `a#[b]`, the result would be: >> >> a.[[PrivateValues]][a.[[DeclarationInfo]] [b] ] >> >> I get that all the square braces can make that hard to read, so here's >> the step by step: >> >> 1. Throw a type error if a is not an object >> 2. Let D be the [[DeclarationInfo]] record of object. >> 3. if D is not in the lexical scope chain of the current function, >> throw a TypeError >> 4. Let N be the value for the key matching b in D, or undefined if no >> such key exists. >> 5. Let P be the [[PrivateValues]] record of object a >> 6. If N is not a key of P, Throw a TypeError >> 7. return P[N] >> >> > Worse, the proposal commingles all private properties across all >> classes. There's nothing in the proposed code stopping you from reading >> and writing private properties defined by class A on instances of an >> unrelated class B. >> >> That's not right either. The example desugaring does indeed throw all >> maps into the same WeakMap. I'll work on that to make a better example. >> However, to get a better understanding of how I see the implementation, you >> should read the implementation details section. There's a much better >> description there. Besides, the 7 step description I just wrote for you >> should help you realize that: >> >> - If the function you're in doesn't know the names of the private >> fields of your object, you get a TypeError. >> - If the object you're accessing doesn't recognize the name of the >> field you're trying to access, you get a TypeError. >> >> So there really is no way to apply the private field names of `class A` >> onto an instance of `class B`. >> >> I'll spend some time tonight re-tooling my example desugaring and a few >> other details I thought of while writing this in the proposal. Hopefully, >> that'll prevent more confusion. >> >> >> On Fri, Jul 27, 2018 at 7:55 PM Waldemar Horwat <waldemar at google.com> >> wrote: >> >>> On 07/26/2018 01:55 PM, Ranando King wrote: >>> > I've just finished updating my proposal with an [Existing proposals]( >>> https://github.com/rdking/proposal-object-members/blob/master/README.md#existing-proposals) >>> section that lists the major differences. >>> >>> Reading the proposal, I'm not yet sure what it's supposed to do. Some >>> things I've noticed: >>> >>> - The proposal commingles static and instance private properties. In >>> the first example, you could read or write this#.field2, and it would >>> work. How would you encode the generic expression a#.[b]? >>> >>> - Worse, the proposal commingles all private properties across all >>> classes. There's nothing in the proposed code stopping you from reading >>> and writing private properties defined by class A on instances of an >>> unrelated class B. >>> >>> Waldemar >>> >> >> _______________________________________________ >> 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/20180728/1e8bd6de/attachment-0001.html>
Ranando, I share your reservations about private fields being bound too tightly to class syntax. In my case it isn’t because I don’t want to use classes, but rather because in the last few years, using the WeakMap solution, a good number of times I’ve needed to do things which the private field proposal either doesn’t permit or doesn’t account for:
- Adding the same “slot” to multiple classes which don’t inherit from each other
- Selectively sharing access to private state through functions declared outside the class body
- Adding slots dynamically, e.g. when adding mix-in methods that may initialize a new slot if necessary when called, since subclassing is not always appropriate
With the WeakMap solution, the privacy mechanism is one that already exists: a scope. This makes it very flexible (it handles the above three cases fine), but it has a key limitation in terms of achieving privacy, which is that global.WeakMap
and WeakMap.prototype
may be compromised. Given this limitation — plus the amount of boilerplate WeakMap privacy can entail — I am very happy to see private instance state being addressed syntactically. However because the model chosen for “scope of privacy” is “class declaration body” — not previously something that provided a closure/scope at all? — instead of just using existing scopes, I have found them impractical to use in some cases.
If I’m understanding your alternative proposal, Ranando, I don’t think it addresses these issues either, not in the way I’m looking for anyway — I’m wishing for a syntactic solution for true private slots on objects, but where said slots are associated with a scope (almost always a module scope) rather than a class declaration. In particular, I’m not convinced that the concept of “protected” makes sense within the JS models of objects and dispatch.
I’m gonna get more detailed about what I see as inadequacies in the current proposal. These are subjective, but not hypothetical: I’ve been doing WeakMap-based privacy for a few years now and I’ve tried converting existing code to use private fields since Chrome shipped it behind a flag. I found that, unfortunately, it did not meet my needs.
Regarding exposing functions that operate on private state but which do not live on the constructor or prototype — there is a way to achieve this in the proposed spec. It’s awkward, but it is technically possible:
class Foo {
#bar = 1;
getBarOfFoo(foo) {
return this.#bar;
}
// [[ ... other methods that may manipulate but do not expose #bar here ... ]]
}
const { getBarOfFoo } = Foo.prototype;
delete Foo.prototype.getBarOfFoo;
It gets more awkward in the “multiple classes with the same semantic slot” case, since one will have to wrap each attempted access in a try-catch, as there is no other way to be certain whether the target has the slot. With WeakMap, in contrast, one will just get undefined — and one may use the same WeakMap to manage the same slot across multiple classes that are declared in the same scope as the WeakMap.
Assume we have two classes with a private bar “slot” which is meant to be semantically equivalent. It holds an integer. We want to create a function that adds together two bar values from any classes that implement this slot. If an argument has no bar slot, bar defaults to zero. With WeakMaps, such a function might look like this:
function addBars(a, b) {
return (Object(wm.get(a)).bar || 0) + (Object(wm.get(b)).bar || 0);
}
Realizing the same logic with classes that use private field syntax is still possible (using the aforementioned “pop off a method” pattern), but now it looks like this:
function addBars(a, b) {
let aBar, bBar;
try {
aBar = getBarOfFoo.call(a);
} catch {
try {
aBar = getBarOfBaz.call(a);
} catch {
aBar = 0;
}
}
try {
bBar = getBarOfFoo.call(b);
} catch {
try {
bBar = getBarOfBaz.call(b);
} catch {
bBar = 0;
}
}
return aBar + bBar;
}
¯\(ツ)/¯
This is a more minor issue, but assuming we can’t have dynamic slots, I would like to take advantage of the fact that whether-a-function-may-access-a-slot is statically knowable by having immediate brand checking occur in all methods that may access private state. This is actually the main source of boilerplate in the WeakMap solution for me, but admittedly I’m probably in a tiny minority here:
set foo(value) {
if (!wm.has(this)) throw new TypeError(`Illegal invocation`);
const str = String(value);
if (VALID_FOO_VALUES.has(str)) {
wm.get(this).foo = str;
} else {
throw new Error(`Invalid value for foo`);
}
}
The difference between the above function with and without the guard concerns guarantees about behavior. The String(value)
call actually might throw, but it ought to be predictable that a method which requires a branded receiver always throws the same error when called on anything unbranded — even if (especially if!) private state access occurs in the method only conditionally, since without the guard, throwing/not-throwing/what-gets-thrown makes an implementation detail observable. The above example is minimal, but there could be more involved state manipulation or observable effects that occur prior to the first private access, possibly leading to being left in an invalid state.
Note that all host and intrinsic functions that may access slots perform these checks. It is the existing pattern in the language for this, and with a syntactic solution, it could be enforced automatically. Right now, with the existing proposal, the boilerplate still exists:
set foo(value) {
try {
this.#foo;
} catch {
throw new TypeError(`Illegal invocation`);
}
const str = String(value);
if (VALID_FOO_VALUES.has(str)) {
this.#foo = str;
} else {
throw new Error(`Invalid value for foo`);
}
}
(You could drop the try-catch if you don’t care whether the error thrown reveals implementation details, but if, like me, you are aiming for behavior matching host APIs, the boilerplate actually increases.)
I can understand if these early checks are deemed undesirable, because they are strictly less flexible than the current proposed behavior, and they would also be incompatible with any solution that allows slots to be added dynamically (unlike the current proposal). However between this and the inability to manage privacy by scope instead of by class declaration body, I will probably find myself sticking with WeakMaps in general (in library code, anyway) because my attempted conversions have often increased rather than reduced complexity and verbosity.
Sorry this is a long post. It’s hard to talk about this subject without getting pretty wordy, but hopefully this is useful feedback about what at least one dev is looking for with private slots. It seems, admittedly, that those of us who need private slots to remain "reflectable" are in the minority.
FWIW I actually love #
syntax though :)
Ranando, I share your reservations about private fields being bound too tightly to class syntax. In my case it isn’t because I don’t want to use classes, but rather because in the last few years, using the WeakMap solution, a good number of times I’ve needed to do things which the private field proposal either doesn’t permit or account for: - Adding the same “slot” to multiple classes which don’t inherit from each other - Selectively sharing access to private state through functions declared outside the class body - Adding slots dynamically, e.g. when adding mix-in methods that may initialize a new slot if necessary when called, since subclassing is not always appropriate With the WeakMap solution, the privacy mechanism is one that already exists: a scope. This makes it very flexible (it handles the above three cases fine), but it has a key limitation in terms of achieving privacy, which is that `global.WeakMap` and `WeakMap.prototype` may be compromised. Given this limitation — plus the amount of boilerplate WeakMap privacy can entail — I am very happy to see private instance state being addressed syntactically. However because the model chosen for “scope of privacy” is “class declaration body” — not previously something that provided a closure/scope at all? — instead of just using existing scopes, I have found them impractical to use in some cases. If I’m understanding your alternative proposal, Ranando, I don’t think it addresses these issues either, not in the way I’m looking for anyway — I’m wishing for a syntactic solution for true private slots on objects, but where said slots are associated with a scope (almost always a module scope) rather than a class declaration. In particular, I’m not convinced that the concept of “protected” makes sense within the JS models of objects and dispatch. I’m gonna get more detailed about what I see as inadequacies in the current proposal. These are subjective, but not hypothetical: I’ve been doing WeakMap-based privacy for a few years now and I’ve tried converting existing code to use private fields since Chrome shipped it behind a flag. I found that, unfortunately, it did not meet my needs. --- Regarding exposing functions that operate on private state but which do not live on the constructor or prototype — there is a way to achieve this in the proposed spec. It’s awkward, but it is technically possible: ```js class Foo { #bar = 1; getBarOfFoo(foo) { return this.#bar; } // [[ ... other methods that may manipulate but do not expose #bar here ... ]] } const { getBarOfFoo } = Foo.prototype; delete Foo.prototype.getBarOfFoo; ``` It gets more awkward in the “multiple classes with the same semantic slot” case, since one will have to wrap each attempted access in a try-catch, as there is no other way to be certain whether the target has the slot. With WeakMap, in contrast, one will just get undefined — and one may use the same WeakMap to manage the same slot across multiple classes that are declared in the same scope as the WeakMap. Assume we have two classes with a private bar “slot” which is meant to be semantically equivalent. It holds an integer. We want to create a function that adds together two bar values from any classes that implement this slot. If an argument has no bar slot, bar defaults to zero. With WeakMaps, such a function might look like this: ```js function addBars(a, b) { return (wm.get(a).bar || 0) + (wm.get(b).bar || 0); } ``` Realizing the same logic with classes that use private field syntax is still possible (using the aforementioned “pop off a method” pattern), but now it looks like this: ```js function addBars(a, b) { let aBar, bBar; try { aBar = getBarOfFoo(a); } catch { try { aBar = getBarOfBaz(a); } catch { aBar = 0; } } try { bBar = getBarOfFoo(b); } catch { try { bBar = getBarOfBaz(b); } catch { bBar = 0; } } return aBar + bBar; } ``` ¯\_(ツ)_/¯ --- This is a more minor issue, but assuming we *can’t* have dynamic slots, I would like to take advantage of the fact that whether-a-function-may-access-a-slot is statically knowable by having immediate brand checking occur in all methods that may access private state. This is actually the main source of boilerplate in the WeakMap solution (for me, but admittedly I’m probably in a tiny minority here): ```js set foo(value) { if (!wm.has(this)) throw new TypeError(`Illegal invocation`); const str = String(value); if (VALID_FOO_VALUES.has(str)) { wm.get(this).foo = str; } else { throw new Error(`Invalid value for foo`); } } ``` The difference between the above function with and without the guard concerns guarantees about behavior. The `String(value)` call actually might throw, but it ought to be predictable that a method which requires a branded receiver always throws the same error when called on anything unbranded — even if (especially if!) private state access occurs in the method only conditionally, since throwing/not-throwing/what-gets-thrown makes an implementation detail observable. The above example is minimal, but there could be more involved state manipulation or observable effects that occur prior to the first private access, possibly leading to being left in an invalid state. Note that all host and intrinsic functions that may access slots perform these checks. It is the existing pattern in the language for this, and with a syntactic solution, it could be enforced automatically. Right now, with the existing proposal, the boilerplate still exists: ```js set foo(value) { try { this.#foo; } catch { throw new TypeError(`Illegal invocation`); } const str = String(value); if (VALID_FOO_VALUES.has(str)) { this.#foo = str; } else { throw new Error(`Invalid value for foo`); } } ``` (You could drop the try-catch if you don’t care whether the error thrown reveals implementation details, but if, like me, you are aiming for behavior matching host APIs, the boilerplate actually increases.) I can understand if these early checks are deemed undesirable, because they are strictly less flexible than the current proposed behavior, and they would also be incompatible with any solution that allows slots to be added dynamically (unlike the current proposal). However between this and the inability to manage privacy by scope instead of by class declaration body, I will probably find myself sticking with WeakMaps in general (in library code, anyway) because my attempted conversions have often increased rather than reduced complexity and verbosity. --- Sorry this is a long post. It’s hard to talk about this subject without getting pretty wordy, but hopefully this is useful feedback about what at least one dev is looking for with private slots. It seems, admittedly, that those of us who need private slots to remain "reflectable" are in the minority. FWIW I actually love `#` syntax though :) -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180728/a8580ecb/attachment.html>
To put this another, much briefer way, here’s a hypothetical, minimalist model for associating private state with objects that would cover me. Privacy would be provided...
- in the form of symbolic keys whose presence cannot be observed (i.e., they would not be exposed by
getOwnPropertySymbols
) without access to the symbol itself - and which have a syntactic declaration so that one can be sure they are really getting private keys (i.e., an api like
Symbol.private()
wouldn’t work)
const bar = private();
// alternatively: const #bar; could be anything so long as it’s syntactic
class Foo {
constructor() {
this[bar] = 1;
}
}
// etc
The keys would be typeof 'symbol'; the only difference being that they are symbols which are flagged as private when created. They would be permitted only in syntactic property assignments and accesses. Existing reflection utilities would disallow the use or appearance of such symbols both to ensure privacy and to maintain the invariant that they are always simple data properties:
Reflect.defineProperty({}, bar, { ... }); // throws type error
Object.getOwnPropertyDescriptors(someObjWithAPrivateSlot); // does not include it
foo[bar] = 2; // fine
This is significantly simpler than what’s in flight both in terms of syntax and mechanics, which makes me suspicious that I’m probably ignoring things that other people find important. However it would bring parity to ES objects wrt being able to implement genuinely private slots in userland with the same flexibility as what is done internally.
In total, this entails a new primary expression, a boolean flag associated with symbol values, and an extra step added to several algorithms associated with Object and Reflect.
To put this another, much briefer way, here’s a hypothetical model for associating private state with objects that would cover me. Privacy would be provided... 1. in the form of symbolic keys whose presence cannot be observed (i.e., they would not be exposed by `getOwnPropertySymbols`) 2. and which have a syntactic declaration so that one can be sure they are really getting private keys (i.e., an api like `Symbol.private()` wouldn’t work) ``` const bar = private(); // alternatively: const #bar; could be anything so long as it’s syntactic class Foo { constructor() { this[bar] = 1; } } // etc ``` The keys would be typeof 'symbol'; the only difference being that they are symbols which are flagged as private when created. They would be permitted only in syntactic property assignments and accesses. Existing reflection utilities would disallow the use or appearance of such symbols both to ensure privacy and to maintain the invariant that they are always simple data properties: ```js Reflect.defineProperty({}, #bar, { ... }); // throws type error Object.getOwnPropertyDescriptors(someObjWithAPrivateSlot); // does not include it foo[bar] = 2; // fine ``` This is significantly simpler than what’s in flight both in terms of syntax and mechanics, which makes me suspicious that I’m probably ignoring things that other people find important. However it would bring parity to ES objects wrt being able to implement genuinely private slots in userland with the same flexibility as what is done internally. In total, this entails a new primary expression, a boolean flag associated with symbol values, and an extra step added to several algorithms associated with Object and Reflect. -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180728/d00dde23/attachment.html>
In a word... wow. You've got me thinking hard here. Those are some peculiar
use cases, and they do a great job of highlighting why someone might forego
using class
. One thing both proposal-class-fields and
proposal-object-members have in common is that the focus is on producing
instance-private fields. All 3 of the scenarios you presented lay outside
of that focus for one reason or another.
Adding the same “slot” to multiple classes which don’t inherit from each
other
I'm a little confused by this one. Are you saying you want multiple non-hierarchally related classes to have an instance private field with shared name, such that the same private field name refers to a distinct and separate field on each instance of every such class, but where any such instance can have that field referenced by that shared name from any member function of the corresponding classes? (Wow that was wordy to write out...) If this is what you meant, you're describing friend classes. The top-down processing nature of ES makes this a difficult thing to create a clean syntax for without risking leaking the private state or fundamentally altering how ES is processed. Mutual friendship is even harder.
... and yet I just thought of a way to do it. By telling you this I'm
leaving myself to consider writing a proposal containing 2 new keywords:
befriend
and friendly
. I don't know if this can be done with the
existing proposal being what it is. However, with my proposal, there's a
chance. The friendly
keyword would declare that an object is prepared to
share select information with any object that befriends it. The befriend
keyword would allow an object to request friendship with an existing
friendly object. I'm not sure this is a good idea, though. This means that
any object declared 'friendly' is automatically insecure as all it takes to
gain access to the selected members of its private space would be to
'befriend' it.
Selectively sharing access to private state through functions declared
outside the class body
The example you gave above still declares the functions in question inside
the class
body, so that's not really a solution. If the example you gave
actually solves your use case, then what you're asking for here isn't even
needed. If, however, that was a bad example, then it sounds like you're
looking for friend functions. See the previous section.
Adding slots dynamically, e.g. when adding mix-in methods that may
initialize a new slot if necessary when called, since subclassing is not always appropriate
Sounds to me like you'd love for class
syntax to look like this:
class [<identifierName1>] [extends <identifierName2>] [mixes
<identifierName3>[, <identifierName3>[, ...]]] { ... }
so that the private fields of the objects in the mixes
list are added to
the set of private fields provided by the class
definition directly. That
would also require another proposal, but I think that can be done
regardless of which instance-private fields proposal gets accepted.
In a word... wow. You've got me thinking hard here. Those are some peculiar use cases, and they do a great job of highlighting why someone might forego using `class`. One thing both proposal-class-fields and proposal-object-members have in common is that the focus is on producing instance-private fields. All 3 of the scenarios you presented lay outside of that focus for one reason or another. > Adding the same “slot” to multiple classes which don’t inherit from each other I'm a little confused by this one. Are you saying you want multiple non-hierarchally related classes to have an instance private field with shared name, such that the same private field name refers to a distinct and separate field on each instance of every such class, but where any such instance can have that field referenced by that shared name from any member function of the corresponding classes? (Wow that was wordy to write out...) If this is what you meant, you're describing friend classes. The top-down processing nature of ES makes this a difficult thing to create a clean syntax for without risking leaking the private state or fundamentally altering how ES is processed. Mutual friendship is even harder. ... and yet I just thought of a way to do it. By telling you this I'm leaving myself to consider writing a proposal containing 2 new keywords: `befriend` and `friendly`. I don't know if this can be done with the existing proposal being what it is. However, with my proposal, there's a chance. The `friendly` keyword would declare that an object is prepared to share select information with any object that befriends it. The `befriend` keyword would allow an object to request friendship with an existing friendly object. I'm not sure this is a good idea, though. This means that any object declared 'friendly' is automatically insecure as all it takes to gain access to the selected members of its private space would be to 'befriend' it. > Selectively sharing access to private state through functions declared outside the class body The example you gave above still declares the functions in question inside the `class` body, so that's not really a solution. If the example you gave actually solves your use case, then what you're asking for here isn't even needed. If, however, that was a bad example, then it sounds like you're looking for friend functions. See the previous section. > Adding slots dynamically, e.g. when adding mix-in methods that may initialize a new slot if necessary when called, since subclassing is not always appropriate Sounds to me like you'd love for `class` syntax to look like this: ```js class [<identifierName1>] [extends <identifierName2>] [mixes <identifierName3>[, <identifierName3>[, ...]]] { ... } ``` so that the private fields of the objects in the `mixes` list are added to the set of private fields provided by the `class` definition directly. That would also require another proposal, but I think that can be done regardless of which instance-private fields proposal gets accepted. On Sat, Jul 28, 2018 at 12:49 PM Darien Valentine <valentinium at gmail.com> wrote: > To put this another, much briefer way, here’s a hypothetical model for > associating private state with objects that would cover me. Privacy would > be provided... > > 1. in the form of symbolic keys whose presence cannot be observed (i.e., > they would not be exposed by `getOwnPropertySymbols`) > 2. and which have a syntactic declaration so that one can be sure they are > really getting private keys (i.e., an api like `Symbol.private()` wouldn’t > work) > > ``` > const bar = private(); > > // alternatively: const #bar; could be anything so long as it’s syntactic > > class Foo { > constructor() { > this[bar] = 1; > } > } > > // etc > ``` > > The keys would be typeof 'symbol'; the only difference being that they are > symbols which are flagged as private when created. They would be permitted > only in syntactic property assignments and accesses. Existing reflection > utilities would disallow the use or appearance of such symbols both to > ensure privacy and to maintain the invariant that they are always simple > data properties: > > ```js > Reflect.defineProperty({}, #bar, { ... }); // throws type error > Object.getOwnPropertyDescriptors(someObjWithAPrivateSlot); // does not > include it > foo[bar] = 2; // fine > ``` > > This is significantly simpler than what’s in flight both in terms of > syntax and mechanics, which makes me suspicious that I’m probably ignoring > things that other people find important. However it would bring parity to > ES objects wrt being able to implement genuinely private slots in userland > with the same flexibility as what is done internally. > > In total, this entails a new primary expression, a boolean flag associated > with symbol values, and an extra step added to several algorithms > associated with Object and Reflect. > _______________________________________________ > 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/20180728/4bb355a4/attachment-0001.html>
Are you saying you want multiple non-hierarchally related classes to have an instance private field with shared name [...]
Yeah. This is a hard problem to solve when trying to integrate private fields with class syntax, but it’s not a problem at all when privacy is a more generic tool based on scope. This also isn’t a foreign concept in ES: consider this intrinsic method:
tc39.github.io/ecma262/#sec-arraybuffer.isview
This method returns true if the argument has the [[ViewedArrayBuffer]]
slot. This slot exists on genuine instances of both %TypedArray%
and %DataView%
, but they do not receive these slots by way of inheritance from a common constructor. There are similar cases in HTML host APIs.
The befriend keyword would allow an object to request friendship with an existing friendly object. I'm not sure this is a good idea, though.
I don’t think it is either, no. It’s too much complexity for too little gain. But again, this is achievable “for free” just by divorcing “private object state” from class declarations (or object literals). I would ask: what problem is solved by making this a feature of the declarations themselves? Does it merit the complexity and the hoop jumping needed to handle edge cases?*
* One person’s edge case; another’s everyday concern haha.
The example you gave above still declares the functions in question inside the class body, so that's not really a solution.
If you’re referring to the first example, that is a demonstration of what is possible using the existing stage 3 class fields proposal as implemented in Chrome. It isn’t what I want; it’s what’s necessary to achieve this with the current stage 3 proposed model.
Sounds to me like you'd love for class syntax to look like this [[example with mixin syntax in declaration]]
Perhaps — it’s interesting for sure! But the pattern that already works, mixin(Cstr)
, is not presently a source of problems for me. Private object state in particular is only made complex by associating it with declarations instead of scopes that happen to contain declarations (or into which constructors are passed, etc). The complexity is artificial — not a good sign imo.
One thing both proposal-class-fields and proposal-object-members have in common is that the focus is on producing instance-private fields. All 3 of the scenarios you presented lay outside of that focus for one reason or another.
Both the WeakMap solution and the stub concept I provided after are more generic than privacy in either of those proposals. When I say "object private state," it’s true that the object in question could be any object. But in practice, any realization of the feature would pertain chiefly to class instances, and the examples I gave, though contrived, do concern class instances. The reason private object state is chiefly an issue of class instances stems directly from the nature of prototype methods and accessors, so if you are not making use of prototypes, you could instead have used a closure+factory directly.
In a nutshell, my issue with existing proposals could probably be summarized as a concern that they are neither as generic nor as simple as native slots. To be clear, proper “slots” are an internal concept, only observable indirectly — but they are the special sauce underlying a number of behaviors which are presently awkward to achieve in ES code itself, and they are a nice simple model of private object state which is tantalizingly close to, but not exactly the same as in two critical ways, symbol keyed properties. That said, “real” slots would continue to have an advantage with regard to cross-realm stuff even if private symbol keys existed.
That such a model is radically simpler — minmax and all that — feels very important to me, but I dunno. I’m not holding my breath for big changes here. The current stage 3 proposal seems to be unstoppable; much smarter / more important people than me have already tried and failed. :)
> Are you saying you want multiple non-hierarchally related classes to have an instance private field with shared name [...] Yeah. This is a hard problem to solve when trying to integrate private fields with class syntax, but it’s not a problem at all when privacy is a more generic tool based on scope. This also isn’t a foreign concept in ES: consider this intrinsic method: https://tc39.github.io/ecma262/#sec-arraybuffer.isview This method returns true if the argument has the `[[ViewedArrayBuffer]]` slot. This slot exists on genuine instances of both `%TypedArray%` and `%DataView%`, but they do not receive these slots by way of inheritance from a common constructor. There are similar cases in HTML host APIs. > The befriend keyword would allow an object to request friendship with an existing friendly object. I'm not sure this is a good idea, though. I don’t think it is either, no. It’s too much complexity for too little gain. But again, this is achievable “for free” just by divorcing “private object state” from class declarations (or object literals). I would ask: what problem is solved by making this a feature of the declarations themselves? Does it merit the complexity and the hoop jumping needed to handle edge cases?\* \* One person’s edge case; another’s everyday concern haha. > The example you gave above still declares the functions in question inside the class body, so that's not really a solution. If you’re referring to the first example, that is a demonstration of what is possible using the existing stage 3 class fields proposal as implemented in Chrome. It isn’t what I want; it’s what’s necessary to achieve this with the current stage 3 proposed model. > Sounds to me like you'd love for class syntax to look like this [[example with mixin syntax in declaration]] Perhaps — it’s interesting for sure! But the pattern that already works, `mixin(Cstr)`, is not presently a source of problems for me. Private object state in particular is only _made complex_ by associating it with declarations instead of scopes that happen to contain declarations (or into which constructors are passed, etc). The complexity is artificial — not a good sign imo. > One thing both proposal-class-fields and proposal-object-members have in common is that the focus is on producing instance-private fields. All 3 of the scenarios you presented lay outside of that focus for one reason or another. Both the WeakMap solution and the stub concept I provided after are more generic than privacy in either of those proposals. When I say "object private state," it’s true that the object in question could be any object. But in practice, any realization of the feature would pertain chiefly to class instances, and the examples I gave, though contrived, do concern class instances. The reason private object state is chiefly an issue of class instances stems directly from the nature of prototype methods and accessors, so if you are not making use of prototypes, you could instead have used a closure+factory directly. --- In a nutshell, my issue with existing proposals could probably be summarized as a concern that they are neither as generic nor as simple as native slots. To be clear, proper “slots” are an internal concept, only observable indirectly — but they are the special sauce underlying a number of behaviors which are presently awkward to achieve in ES code itself, and they are a nice simple model of private object state which is tantalizingly close to, but not _exactly_ the same as in two critical ways, symbol keyed properties. That said, “real” slots would continue to have an advantage with regard to cross-realm stuff even if private symbol keys existed. That such a model is radically simpler — minmax and all that — feels very important to me, but I dunno. I’m not holding my breath for big changes here. The current stage 3 proposal seems to be unstoppable; much smarter / more important people than me have already tried and failed. :) On Sat, Jul 28, 2018 at 3:14 PM Ranando King <kingmph at gmail.com> wrote: > In a word... wow. You've got me thinking hard here. Those are some > peculiar use cases, and they do a great job of highlighting why someone > might forego using `class`. One thing both proposal-class-fields and > proposal-object-members have in common is that the focus is on producing > instance-private fields. All 3 of the scenarios you presented lay outside > of that focus for one reason or another. > > > Adding the same “slot” to multiple classes which don’t inherit from each > other > > I'm a little confused by this one. Are you saying you want multiple > non-hierarchally related classes to have an instance private field with > shared name, such that the same private field name refers to a distinct and > separate field on each instance of every such class, but where any such > instance can have that field referenced by that shared name from any member > function of the corresponding classes? (Wow that was wordy to write out...) > If this is what you meant, you're describing friend classes. The top-down > processing nature of ES makes this a difficult thing to create a clean > syntax for without risking leaking the private state or fundamentally > altering how ES is processed. Mutual friendship is even harder. > > ... and yet I just thought of a way to do it. By telling you this I'm > leaving myself to consider writing a proposal containing 2 new keywords: > `befriend` and `friendly`. I don't know if this can be done with the > existing proposal being what it is. However, with my proposal, there's a > chance. The `friendly` keyword would declare that an object is prepared to > share select information with any object that befriends it. The `befriend` > keyword would allow an object to request friendship with an existing > friendly object. I'm not sure this is a good idea, though. This means that > any object declared 'friendly' is automatically insecure as all it takes to > gain access to the selected members of its private space would be to > 'befriend' it. > > > Selectively sharing access to private state through functions declared > outside the class body > > The example you gave above still declares the functions in question inside > the `class` body, so that's not really a solution. If the example you gave > actually solves your use case, then what you're asking for here isn't even > needed. If, however, that was a bad example, then it sounds like you're > looking for friend functions. See the previous section. > > > Adding slots dynamically, e.g. when adding mix-in methods that may > initialize a new slot if necessary when called, since subclassing is not > always appropriate > > Sounds to me like you'd love for `class` syntax to look like this: > > ```js > class [<identifierName1>] [extends <identifierName2>] [mixes > <identifierName3>[, <identifierName3>[, ...]]] { ... } > ``` > so that the private fields of the objects in the `mixes` list are added to > the set of private fields provided by the `class` definition directly. That > would also require another proposal, but I think that can be done > regardless of which instance-private fields proposal gets accepted. > > On Sat, Jul 28, 2018 at 12:49 PM Darien Valentine <valentinium at gmail.com> > wrote: > >> To put this another, much briefer way, here’s a hypothetical model for >> associating private state with objects that would cover me. Privacy would >> be provided... >> >> 1. in the form of symbolic keys whose presence cannot be observed (i.e., >> they would not be exposed by `getOwnPropertySymbols`) >> 2. and which have a syntactic declaration so that one can be sure they >> are really getting private keys (i.e., an api like `Symbol.private()` >> wouldn’t work) >> >> ``` >> const bar = private(); >> >> // alternatively: const #bar; could be anything so long as it’s syntactic >> >> class Foo { >> constructor() { >> this[bar] = 1; >> } >> } >> >> // etc >> ``` >> >> The keys would be typeof 'symbol'; the only difference being that they >> are symbols which are flagged as private when created. They would be >> permitted only in syntactic property assignments and accesses. Existing >> reflection utilities would disallow the use or appearance of such symbols >> both to ensure privacy and to maintain the invariant that they are always >> simple data properties: >> >> ```js >> Reflect.defineProperty({}, #bar, { ... }); // throws type error >> Object.getOwnPropertyDescriptors(someObjWithAPrivateSlot); // does not >> include it >> foo[bar] = 2; // fine >> ``` >> >> This is significantly simpler than what’s in flight both in terms of >> syntax and mechanics, which makes me suspicious that I’m probably ignoring >> things that other people find important. However it would bring parity to >> ES objects wrt being able to implement genuinely private slots in userland >> with the same flexibility as what is done internally. >> >> In total, this entails a new primary expression, a boolean flag >> associated with symbol values, and an extra step added to several >> algorithms associated with Object and Reflect. >> _______________________________________________ >> 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/20180728/c1be94ac/attachment-0001.html>
I've almost given up on making any significant headway in either adjusting or flat-out correcting the flaws in that proposal, but I don't intend to stop trying until either we get stuck with that proposal, or they understand and accept what I'm telling them, or logically prove that my concerns are either irrational or inconsequential.
Private object state in particular is only made complex by associating
it with declarations instead of scopes that happen to contain declarations (or into which constructors are passed, etc). The complexity is artificial — not a good sign imo.
That's not quite right. What you're essentially asking for is a violatable private field, or as has been described by others, a "soft private". Since we agree that the "friendly" & "befriend" pair is a somewhat (if not completely) bad idea, I'm going to take 1 more pass at your 3 requests with a different angle.
Adding the same “slot” to multiple classes which don’t inherit from each
other
Selectively sharing access to private state through functions declared
outside the class body
//Using my proposal
var {A, B, C} = (() => {
const common = Symbol("common");
class A {
private [common] = 1;
add(...args) {
var retval = this#[common];
for (let obj of args) {
retval += obj#[common];
}
return retval;
}
}
class B {
private [common] = 2;
optional() {
console.log(`common member = ${this#[common]}`);
}
}
var C = {
private [common]: 3,
required() {
console.log(`common member = ${this#[common]}`);
}
}
return { A, B, C };
})();
//So you want the following statement to not throw a TypeError and return 6
(new A()).add(new B(), C);
I'm not sure I can make this work in my proposal, and I'm absolutely sure
you'd be flatly refused by the other proposal. If a Symbol
is provided as
the [[IdentifierName]]
of a private or protected field, then I can let
that Symbol
be both the key and value that are added to the
[[DeclarationInfo]]
and [[InheritanceInfo]]
records. That way there
will be a common private field name usable by all 3 objects. However, the
guardian logic tries to verify that the function trying to access the
private fields of an instance is a member of the same or descending
prototype that was used to create that instance. If I removed that
requirement, it would work. However, there'd be no way to keep the private
data from being leaked. Sadly, it's all or nothing with this approach. Hard
private or soft private, those are the only choices. The TC39 board has
already decided that what they want new syntax for is hard private.
Adding slots dynamically, e.g. when adding mix-in methods that may
initialize a new slot if necessary when called, since subclassing is not always appropriate
Because the TC39 board has set their sights on hard private, this will
require new syntax like what I suggested earlier Adding private members
dynamically would also pose a leak risk if it could be done after the
prototype has been fully constructed. The main reason the privacy is set on
a declaration level is because scope-level inheritance isn't very good for
class
-oriented inheritance. The class
keyword was provided to simplify
the vertical inheritance model, along with some API to enable inheritance
from native objects even without using class
. The syntax changes for
simplifying private field declaration are just an extension of that. Even
though it's not unusual for some developers to spend a lot of time working
with fringe use-cases, syntax changes are almost always going to be made
for the most common use cases first. Maybe after the private fields problem
has been resolved, someone will figure out a better way to handle your use
cases.
I've almost given up on making any significant headway in either adjusting or flat-out correcting the flaws in that proposal, but I don't intend to stop trying until either we get stuck with that proposal, or they understand and accept what I'm telling them, or logically prove that my concerns are either irrational or inconsequential. > Private object state in particular is only _made complex_ by associating it with declarations instead of scopes that happen to contain declarations (or into which constructors are passed, etc). The complexity is artificial — not a good sign imo. That's not quite right. What you're essentially asking for is a violatable private field, or as has been described by others, a "soft private". Since we agree that the "friendly" & "befriend" pair is a somewhat (if not completely) bad idea, I'm going to take 1 more pass at your 3 requests with a different angle. > Adding the same “slot” to multiple classes which don’t inherit from each other > Selectively sharing access to private state through functions declared outside the class body ```js //Using my proposal var {A, B, C} = (() => { const common = Symbol("common"); class A { private [common] = 1; add(...args) { var retval = this#[common]; for (let obj of args) { retval += obj#[common]; } return retval; } } class B { private [common] = 2; optional() { console.log(`common member = ${this#[common]}`); } } var C = { private [common]: 3, required() { console.log(`common member = ${this#[common]}`); } } return { A, B, C }; })(); //So you want the following statement to not throw a TypeError and return 6 (new A()).add(new B(), C); ``` I'm not sure I can make this work in my proposal, and I'm absolutely sure you'd be flatly refused by the other proposal. If a `Symbol` is provided as the `[[IdentifierName]]` of a private or protected field, then I can let that `Symbol` be both the key and value that are added to the `[[DeclarationInfo]]` and `[[InheritanceInfo]]` records. That way there will be a common private field name usable by all 3 objects. However, the guardian logic tries to verify that the function trying to access the private fields of an instance is a member of the same or descending prototype that was used to create that instance. If I removed that requirement, it would work. However, there'd be no way to keep the private data from being leaked. Sadly, it's all or nothing with this approach. Hard private or soft private, those are the only choices. The TC39 board has already decided that what they want new syntax for is hard private. > Adding slots dynamically, e.g. when adding mix-in methods that may initialize a new slot if necessary when called, since subclassing is not always appropriate Because the TC39 board has set their sights on hard private, this will require new syntax like what I suggested earlier Adding private members dynamically would also pose a leak risk if it could be done after the prototype has been fully constructed. The main reason the privacy is set on a declaration level is because scope-level inheritance isn't very good for `class`-oriented inheritance. The `class` keyword was provided to simplify the vertical inheritance model, along with some API to enable inheritance from native objects even without using `class`. The syntax changes for simplifying private field declaration are just an extension of that. Even though it's not unusual for some developers to spend a lot of time working with fringe use-cases, syntax changes are almost always going to be made for the most common use cases first. Maybe after the private fields problem has been resolved, someone will figure out a better way to handle your use cases. On Sat, Jul 28, 2018 at 3:52 PM Darien Valentine <valentinium at gmail.com> wrote: > > Are you saying you want multiple non-hierarchally related classes to > have an instance private field with shared name [...] > > Yeah. This is a hard problem to solve when trying to integrate private > fields with class syntax, but it’s not a problem at all when privacy is a > more generic tool based on scope. This also isn’t a foreign concept in ES: > consider this intrinsic method: > > https://tc39.github.io/ecma262/#sec-arraybuffer.isview > > This method returns true if the argument has the `[[ViewedArrayBuffer]]` > slot. This slot exists on genuine instances of both `%TypedArray%` and > `%DataView%`, but they do not receive these slots by way of inheritance > from a common constructor. There are similar cases in HTML host APIs. > > > The befriend keyword would allow an object to request friendship with an > existing friendly object. I'm not sure this is a good idea, though. > > I don’t think it is either, no. It’s too much complexity for too little > gain. But again, this is achievable “for free” just by divorcing “private > object state” from class declarations (or object literals). I would ask: > what problem is solved by making this a feature of the declarations > themselves? Does it merit the complexity and the hoop jumping needed to > handle edge cases?\* > > \* One person’s edge case; another’s everyday concern haha. > > > The example you gave above still declares the functions in question > inside the class body, so that's not really a solution. > > If you’re referring to the first example, that is a demonstration of what > is possible using the existing stage 3 class fields proposal as implemented > in Chrome. It isn’t what I want; it’s what’s necessary to achieve this with > the current stage 3 proposed model. > > > Sounds to me like you'd love for class syntax to look like this > [[example with mixin syntax in declaration]] > > Perhaps — it’s interesting for sure! But the pattern that already works, > `mixin(Cstr)`, is not presently a source of problems for me. Private object > state in particular is only _made complex_ by associating it with > declarations instead of scopes that happen to contain declarations (or into > which constructors are passed, etc). The complexity is artificial — not a > good sign imo. > > > One thing both proposal-class-fields and proposal-object-members have > in common is that the focus is on producing instance-private fields. All 3 > of the scenarios you presented lay outside of that focus for one reason or > another. > > Both the WeakMap solution and the stub concept I provided after are more > generic than privacy in either of those proposals. When I say "object > private state," it’s true that the object in question could be any object. > But in practice, any realization of the feature would pertain chiefly to > class instances, and the examples I gave, though contrived, do concern > class instances. The reason private object state is chiefly an issue of > class instances stems directly from the nature of prototype methods and > accessors, so if you are not making use of prototypes, you could instead > have used a closure+factory directly. > > --- > > In a nutshell, my issue with existing proposals could probably be > summarized as a concern that they are neither as generic nor as simple as > native slots. To be clear, proper “slots” are an internal concept, only > observable indirectly — but they are the special sauce underlying a number > of behaviors which are presently awkward to achieve in ES code itself, and > they are a nice simple model of private object state which is tantalizingly > close to, but not _exactly_ the same as in two critical ways, symbol keyed > properties. That said, “real” slots would continue to have an advantage > with regard to cross-realm stuff even if private symbol keys existed. > > That such a model is radically simpler — minmax and all that — feels very > important to me, but I dunno. I’m not holding my breath for big changes > here. The current stage 3 proposal seems to be unstoppable; much smarter / > more important people than me have already tried and failed. :) > > > On Sat, Jul 28, 2018 at 3:14 PM Ranando King <kingmph at gmail.com> wrote: > >> In a word... wow. You've got me thinking hard here. Those are some >> peculiar use cases, and they do a great job of highlighting why someone >> might forego using `class`. One thing both proposal-class-fields and >> proposal-object-members have in common is that the focus is on producing >> instance-private fields. All 3 of the scenarios you presented lay outside >> of that focus for one reason or another. >> >> > Adding the same “slot” to multiple classes which don’t inherit from >> each other >> >> I'm a little confused by this one. Are you saying you want multiple >> non-hierarchally related classes to have an instance private field with >> shared name, such that the same private field name refers to a distinct and >> separate field on each instance of every such class, but where any such >> instance can have that field referenced by that shared name from any member >> function of the corresponding classes? (Wow that was wordy to write out...) >> If this is what you meant, you're describing friend classes. The top-down >> processing nature of ES makes this a difficult thing to create a clean >> syntax for without risking leaking the private state or fundamentally >> altering how ES is processed. Mutual friendship is even harder. >> >> ... and yet I just thought of a way to do it. By telling you this I'm >> leaving myself to consider writing a proposal containing 2 new keywords: >> `befriend` and `friendly`. I don't know if this can be done with the >> existing proposal being what it is. However, with my proposal, there's a >> chance. The `friendly` keyword would declare that an object is prepared to >> share select information with any object that befriends it. The `befriend` >> keyword would allow an object to request friendship with an existing >> friendly object. I'm not sure this is a good idea, though. This means that >> any object declared 'friendly' is automatically insecure as all it takes to >> gain access to the selected members of its private space would be to >> 'befriend' it. >> >> > Selectively sharing access to private state through functions declared >> outside the class body >> >> The example you gave above still declares the functions in question >> inside the `class` body, so that's not really a solution. If the example >> you gave actually solves your use case, then what you're asking for here >> isn't even needed. If, however, that was a bad example, then it sounds like >> you're looking for friend functions. See the previous section. >> >> > Adding slots dynamically, e.g. when adding mix-in methods that may >> initialize a new slot if necessary when called, since subclassing is not >> always appropriate >> >> Sounds to me like you'd love for `class` syntax to look like this: >> >> ```js >> class [<identifierName1>] [extends <identifierName2>] [mixes >> <identifierName3>[, <identifierName3>[, ...]]] { ... } >> ``` >> so that the private fields of the objects in the `mixes` list are added >> to the set of private fields provided by the `class` definition directly. >> That would also require another proposal, but I think that can be done >> regardless of which instance-private fields proposal gets accepted. >> >> On Sat, Jul 28, 2018 at 12:49 PM Darien Valentine <valentinium at gmail.com> >> wrote: >> >>> To put this another, much briefer way, here’s a hypothetical model for >>> associating private state with objects that would cover me. Privacy would >>> be provided... >>> >>> 1. in the form of symbolic keys whose presence cannot be observed (i.e., >>> they would not be exposed by `getOwnPropertySymbols`) >>> 2. and which have a syntactic declaration so that one can be sure they >>> are really getting private keys (i.e., an api like `Symbol.private()` >>> wouldn’t work) >>> >>> ``` >>> const bar = private(); >>> >>> // alternatively: const #bar; could be anything so long as it’s syntactic >>> >>> class Foo { >>> constructor() { >>> this[bar] = 1; >>> } >>> } >>> >>> // etc >>> ``` >>> >>> The keys would be typeof 'symbol'; the only difference being that they >>> are symbols which are flagged as private when created. They would be >>> permitted only in syntactic property assignments and accesses. Existing >>> reflection utilities would disallow the use or appearance of such symbols >>> both to ensure privacy and to maintain the invariant that they are always >>> simple data properties: >>> >>> ```js >>> Reflect.defineProperty({}, #bar, { ... }); // throws type error >>> Object.getOwnPropertyDescriptors(someObjWithAPrivateSlot); // does not >>> include it >>> foo[bar] = 2; // fine >>> ``` >>> >>> This is significantly simpler than what’s in flight both in terms of >>> syntax and mechanics, which makes me suspicious that I’m probably ignoring >>> things that other people find important. However it would bring parity to >>> ES objects wrt being able to implement genuinely private slots in userland >>> with the same flexibility as what is done internally. >>> >>> In total, this entails a new primary expression, a boolean flag >>> associated with symbol values, and an extra step added to several >>> algorithms associated with Object and Reflect. >>> _______________________________________________ >>> 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/20180728/ad7aaf1e/attachment-0001.html>
What you're essentially asking for is a violatable private field, or as has been described by others, a "soft private".
We might have different definitions here, but I would describe what I’m talking about as hard private. Soft private, at least as it appears to have been defined in prior discussions, described an avenue where symbol keyed properties were given a new syntactic form — but they were still just regular symbol keys, and therefore could be introspected by outside agents who had not been given express privilege to do so:
[...] the core would be that "private state" is simply (public) symbol-named properties, with syntactic sugar for those symbols, and possibly some kind of introspection over them [...]
The thread goes on to contrast the soft model with an earlier version of the private fields proposal seen today. The hard private example uses the class declaration as a pseudo-scope, but contrasting these two options as if they are binary is not accurate: hard private through module/function/block scope already exists, it is just difficult to work with in the context of shared prototypes — one must either use WeakMaps, technically giving up hardness because of the forgeability of global.WeakMap
/ WeakMap.prototype
/ WeakMap.prototype.get|has|set
, or be willing to either not worry about garbage collection or implement it manually. This could be solved for with a few rather undramatic changes, though.
Notably, the first post there lists the following as a disadvantage of the soft model it describes:
Platform objects, both within ECMAScript and in embedding environments, contain hard private state. If a library wants to be high-fidelity and just like a platform object, soft-private state does not provide this (@domenic)
...but neither model there quite covers that use case. Platform objects can see each other’s private state (cf the isView
example earlier, or scan the DOM API specs / Chrome source a bit to find numerous examples). It’s only the ES layer interacting with their interfaces that cannot.
Such things can be achieved with ordinary scope, which is why the WeakMap pattern has worked in practice in my experience to date, while class-declaration-scoped privacy has not. It isn’t uncommon for a library’s exposed interface to be composed of an object graph, where privacy is a concern at this public interface level, but library internal state may be interconnected in unexposed ways under the hood. The most familiar example of this is a DOM node tree. As an experiment, perhaps try to implement the relationships between HTMLFormElement, HTMLFormControlsCollection and the various form control elements using either the main private fields proposal or your alternative proposal and see what happens.
However, the guardian logic tries to verify that the function trying to access the private fields of an instance is a member of the same or descending prototype that was used to create that instance.
Because I’m looking at this in terms of slots, I’d first point out that prototypes don’t determine slottedness, the execution of some specific constructor does. It’s during this process that slots are associated with the newly minted object by its identity. But even the current private fields proposal tracks this behavior closely, and I’m not sure how else it could work. The [[Prototype]] slot of an object is typically mutable (R|O.setPrototypeOf
, __proto__
) and forgeable (Proxy’s getPrototypeOf
trap). Why/how would its value matter when it comes to accessing private state?
const pattern = /foo/;
Reflect.setPrototypeOf(pattern, Date.prototype);
pattern instanceof Date; // true
pattern instanceof RegExp; // false
pattern.getMinutes(); // throws TypeError because [[DateValue]] slot is missing
RegExp.prototype.exec.call(pattern, 'foo'); // works; object has RegExp private slots
If I removed that requirement, it would work. However, there'd be no way to keep the private data from being leaked. Sadly, it's all or nothing with this approach. Hard private or soft private, those are the only choices.
In the context of what you’ve described here this may be true, but no such limitation presently exists. We can already do all this — hard, leak-free privacy, brandedness, “friends” etc — with scopes and WeakMaps, but for the fact that the WeakMap
intrinsics may be forged. So what’s baffled me is this: why are all the proposals exploring this space not addressing that relatively simple existing problem, and instead starting off from a place of significant new complexity? You said “maybe after the private fields problem has been resolved, someone will figure out a better way to handle your use cases,” but I’d have hoped for the opposite — I want the primitive building blocks which things like class field syntax could be built over, if it is found that they are still necessary once the root issue is solved for.
The main reason the privacy is set on a declaration level is because scope-level inheritance isn't very good for class-oriented inheritance.
Can you explain this more? I’m not sure what’s meant by “scope-level inheritance” here.
I don't intend to stop [...]
I very much admire your dedication! I’m also digging the discussion. I think we may be representing viewpoints at opposite extremes here, so it’s an interesting contrast, but it also probably means we may be lacking some context for understanding one another’s angles. I’d be curious to hear more about what you see as the problems with the current fields proposal + how your members proposal would solve them; the repo readme didn’t seem to include a rationale section.
> What you're essentially asking for is a violatable private field, or as has been described by others, a "soft private". We might have different definitions here, but I would describe what I’m talking about as hard private. Soft private, at least as it appears to have been defined in [prior discussions]( https://github.com/tc39/proposal-private-fields/issues/33), described an avenue where symbol keyed properties were given a new syntactic form — but they were still just regular symbol keys, and therefore could be introspected by outside agents who had not been given express privilege to do so: > [...] the core would be that "private state" is simply (public) symbol-named properties, with syntactic sugar for those symbols, and possibly some kind of introspection over them [...] The thread goes on to contrast the soft model with an earlier version of the private fields proposal seen today. The hard private example uses the class declaration as a pseudo-scope, but contrasting these two options as if they are binary is not accurate: hard private through module/function/block scope already exists, it is just difficult to work with in the context of shared prototypes — one must either use WeakMaps, technically giving _hardness_ because of the forgeability of `global.WeakMap` / `WeakMap.prototype` / `WeakMap.prototype.get|has|set`, or be willing to either not worry about garbage collection or implement it manually. This could be solved for with a few rather undramatic changes, though. Notably, the first post there lists the following as a disadvantage of the soft model it describes: > Platform objects, both within ECMAScript and in embedding environments, contain hard private state. If a library wants to be high-fidelity and just like a platform object, soft-private state does not provide this (@domenic) ...but neither model there quite covers that use case. Platform objects _can_ see each other’s private state (cf the `isView` example earlier, or scan the DOM API specs / Chrome source a bit to find numerous examples). It’s only the ES layer interacting with their interfaces that cannot. Such things can be achieved with ordinary scope, which is why the WeakMap pattern has worked in practice in my experience to date, while class-declaration-scoped privacy has not. It isn’t uncommon for a library’s exposed interface to be composed of an object graph, where privacy is a concern at this public interface level, but library internal state may be interconnected in unexposed ways under the hood. The most familiar example of this is a DOM node tree. As an experiment, perhaps try to implement the relationships between HTMLFormElement, HTMLFormControlsCollection and the various form control elements using either the main private fields proposal or your alternative proposal and see what happens. > However, the guardian logic tries to verify that the function trying to access the private fields of an instance is a member of the same or descending prototype that was used to create that instance. Because I’m looking at this in terms of slots, I’d first point out that prototypes don’t determine slottedness, the execution of some specific constructor does. It’s during this process that slots are associated with the newly minted object by its identity. But even the current private fields proposal tracks this behavior closely, and I’m not sure how else it could work. The [[Prototype]] slot of an object is typically mutable (`R|O.setPrototypeOf`, `__proto__`) and forgeable (Proxy’s `getPrototypeOf` trap). Why/how would its value matter when it comes to accessing private state? ```js const pattern = /foo/; Reflect.setPrototypeOf(pattern, Date.prototype); pattern instanceof Date; // true pattern instanceof RegExp; // false pattern.getMinutes(); // throws TypeError because [[DateValue]] slot is missing RegExp.prototype.exec.call(pattern, 'foo'); // works; object has RegExp private slots ``` > If I removed that requirement, it would work. However, there'd be no way to keep the private data from being leaked. Sadly, it's all or nothing with this approach. Hard private or soft private, those are the only choices. In the context of what you’ve described here this may be true, but no such limitation presently exists. We can already do all this — hard, leak-free privacy, brandedness, “friends” etc — with scopes and WeakMaps, but for the fact that the `WeakMap` intrinsics may be forged. So what’s baffled me is this: why are all the proposals exploring this space not addressing that relatively simple existing problem, and instead starting off from a place of significant new complexity? You said “maybe after the private fields problem has been resolved, someone will figure out a better way to handle your use cases,” but I’d have hoped for the opposite — I want the primitive building blocks which things like class field syntax could be built over, if it is found that they are still necessary once the root issue is solved for. > The main reason the privacy is set on a declaration level is because scope-level inheritance isn't very good for class-oriented inheritance. Can you explain this more? I’m not sure what’s meant by “scope-level inheritance” here. > I don't intend to stop [...] I very much admire your dedication! I’m also digging the discussion. I think we may be representing viewpoints at opposite extremes here, so it’s an interesting contrast, but it also probably means we may be lacking some context for understanding one another’s angles. I’d be curious to hear more about what you see as the problems with the current fields proposal + how your members proposal would solve them; the repo readme didn’t seem to include a rationale section. On Sat, Jul 28, 2018 at 10:30 PM Ranando King <kingmph at gmail.com> wrote: > I've almost given up on making any significant headway in either adjusting > or flat-out correcting the flaws in that proposal, but I don't intend to > stop trying until either we get stuck with that proposal, or they > understand and accept what I'm telling them, or logically prove that my > concerns are either irrational or inconsequential. > > > Private object state in particular is only _made complex_ by > associating it with declarations instead of scopes that happen to contain > declarations (or into which constructors are passed, etc). The complexity > is artificial — not a good sign imo. > > That's not quite right. What you're essentially asking for is a violatable > private field, or as has been described by others, a "soft private". Since > we agree that the "friendly" & "befriend" pair is a somewhat (if not > completely) bad idea, I'm going to take 1 more pass at your 3 requests with > a different angle. > > > Adding the same “slot” to multiple classes which don’t inherit from each > other > > Selectively sharing access to private state through functions declared > outside the class body > > ```js > //Using my proposal > var {A, B, C} = (() => { > const common = Symbol("common"); > > class A { > private [common] = 1; > add(...args) { > var retval = this#[common]; > for (let obj of args) { > retval += obj#[common]; > } > return retval; > } > } > class B { > private [common] = 2; > optional() { > console.log(`common member = ${this#[common]}`); > } > } > var C = { > private [common]: 3, > required() { > console.log(`common member = ${this#[common]}`); > } > } > > return { A, B, C }; > })(); > > //So you want the following statement to not throw a TypeError and return 6 > (new A()).add(new B(), C); > ``` > I'm not sure I can make this work in my proposal, and I'm absolutely sure > you'd be flatly refused by the other proposal. If a `Symbol` is provided as > the `[[IdentifierName]]` of a private or protected field, then I can let > that `Symbol` be both the key and value that are added to the > `[[DeclarationInfo]]` and `[[InheritanceInfo]]` records. That way there > will be a common private field name usable by all 3 objects. However, the > guardian logic tries to verify that the function trying to access the > private fields of an instance is a member of the same or descending > prototype that was used to create that instance. If I removed that > requirement, it would work. However, there'd be no way to keep the private > data from being leaked. Sadly, it's all or nothing with this approach. Hard > private or soft private, those are the only choices. The TC39 board has > already decided that what they want new syntax for is hard private. > > > Adding slots dynamically, e.g. when adding mix-in methods that may > initialize a new slot if necessary when called, since subclassing is not > always appropriate > > Because the TC39 board has set their sights on hard private, this will > require new syntax like what I suggested earlier Adding private members > dynamically would also pose a leak risk if it could be done after the > prototype has been fully constructed. The main reason the privacy is set on > a declaration level is because scope-level inheritance isn't very good for > `class`-oriented inheritance. The `class` keyword was provided to simplify > the vertical inheritance model, along with some API to enable inheritance > from native objects even without using `class`. The syntax changes for > simplifying private field declaration are just an extension of that. Even > though it's not unusual for some developers to spend a lot of time working > with fringe use-cases, syntax changes are almost always going to be made > for the most common use cases first. Maybe after the private fields problem > has been resolved, someone will figure out a better way to handle your use > cases. > > > On Sat, Jul 28, 2018 at 3:52 PM Darien Valentine <valentinium at gmail.com> > wrote: > >> > Are you saying you want multiple non-hierarchally related classes to >> have an instance private field with shared name [...] >> >> Yeah. This is a hard problem to solve when trying to integrate private >> fields with class syntax, but it’s not a problem at all when privacy is a >> more generic tool based on scope. This also isn’t a foreign concept in ES: >> consider this intrinsic method: >> >> https://tc39.github.io/ecma262/#sec-arraybuffer.isview >> >> This method returns true if the argument has the `[[ViewedArrayBuffer]]` >> slot. This slot exists on genuine instances of both `%TypedArray%` and >> `%DataView%`, but they do not receive these slots by way of inheritance >> from a common constructor. There are similar cases in HTML host APIs. >> >> > The befriend keyword would allow an object to request friendship with >> an existing friendly object. I'm not sure this is a good idea, though. >> >> I don’t think it is either, no. It’s too much complexity for too little >> gain. But again, this is achievable “for free” just by divorcing “private >> object state” from class declarations (or object literals). I would ask: >> what problem is solved by making this a feature of the declarations >> themselves? Does it merit the complexity and the hoop jumping needed to >> handle edge cases?\* >> >> \* One person’s edge case; another’s everyday concern haha. >> >> > The example you gave above still declares the functions in question >> inside the class body, so that's not really a solution. >> >> If you’re referring to the first example, that is a demonstration of what >> is possible using the existing stage 3 class fields proposal as implemented >> in Chrome. It isn’t what I want; it’s what’s necessary to achieve this with >> the current stage 3 proposed model. >> >> > Sounds to me like you'd love for class syntax to look like this >> [[example with mixin syntax in declaration]] >> >> Perhaps — it’s interesting for sure! But the pattern that already works, >> `mixin(Cstr)`, is not presently a source of problems for me. Private object >> state in particular is only _made complex_ by associating it with >> declarations instead of scopes that happen to contain declarations (or into >> which constructors are passed, etc). The complexity is artificial — not a >> good sign imo. >> >> > One thing both proposal-class-fields and proposal-object-members have >> in common is that the focus is on producing instance-private fields. All 3 >> of the scenarios you presented lay outside of that focus for one reason or >> another. >> >> Both the WeakMap solution and the stub concept I provided after are more >> generic than privacy in either of those proposals. When I say "object >> private state," it’s true that the object in question could be any object. >> But in practice, any realization of the feature would pertain chiefly to >> class instances, and the examples I gave, though contrived, do concern >> class instances. The reason private object state is chiefly an issue of >> class instances stems directly from the nature of prototype methods and >> accessors, so if you are not making use of prototypes, you could instead >> have used a closure+factory directly. >> >> --- >> >> In a nutshell, my issue with existing proposals could probably be >> summarized as a concern that they are neither as generic nor as simple as >> native slots. To be clear, proper “slots” are an internal concept, only >> observable indirectly — but they are the special sauce underlying a number >> of behaviors which are presently awkward to achieve in ES code itself, and >> they are a nice simple model of private object state which is tantalizingly >> close to, but not _exactly_ the same as in two critical ways, symbol keyed >> properties. That said, “real” slots would continue to have an advantage >> with regard to cross-realm stuff even if private symbol keys existed. >> >> That such a model is radically simpler — minmax and all that — feels very >> important to me, but I dunno. I’m not holding my breath for big changes >> here. The current stage 3 proposal seems to be unstoppable; much smarter / >> more important people than me have already tried and failed. :) >> >> >> On Sat, Jul 28, 2018 at 3:14 PM Ranando King <kingmph at gmail.com> wrote: >> >>> In a word... wow. You've got me thinking hard here. Those are some >>> peculiar use cases, and they do a great job of highlighting why someone >>> might forego using `class`. One thing both proposal-class-fields and >>> proposal-object-members have in common is that the focus is on producing >>> instance-private fields. All 3 of the scenarios you presented lay outside >>> of that focus for one reason or another. >>> >>> > Adding the same “slot” to multiple classes which don’t inherit from >>> each other >>> >>> I'm a little confused by this one. Are you saying you want multiple >>> non-hierarchally related classes to have an instance private field with >>> shared name, such that the same private field name refers to a distinct and >>> separate field on each instance of every such class, but where any such >>> instance can have that field referenced by that shared name from any member >>> function of the corresponding classes? (Wow that was wordy to write out...) >>> If this is what you meant, you're describing friend classes. The top-down >>> processing nature of ES makes this a difficult thing to create a clean >>> syntax for without risking leaking the private state or fundamentally >>> altering how ES is processed. Mutual friendship is even harder. >>> >>> ... and yet I just thought of a way to do it. By telling you this I'm >>> leaving myself to consider writing a proposal containing 2 new keywords: >>> `befriend` and `friendly`. I don't know if this can be done with the >>> existing proposal being what it is. However, with my proposal, there's a >>> chance. The `friendly` keyword would declare that an object is prepared to >>> share select information with any object that befriends it. The `befriend` >>> keyword would allow an object to request friendship with an existing >>> friendly object. I'm not sure this is a good idea, though. This means that >>> any object declared 'friendly' is automatically insecure as all it takes to >>> gain access to the selected members of its private space would be to >>> 'befriend' it. >>> >>> > Selectively sharing access to private state through functions declared >>> outside the class body >>> >>> The example you gave above still declares the functions in question >>> inside the `class` body, so that's not really a solution. If the example >>> you gave actually solves your use case, then what you're asking for here >>> isn't even needed. If, however, that was a bad example, then it sounds like >>> you're looking for friend functions. See the previous section. >>> >>> > Adding slots dynamically, e.g. when adding mix-in methods that may >>> initialize a new slot if necessary when called, since subclassing is not >>> always appropriate >>> >>> Sounds to me like you'd love for `class` syntax to look like this: >>> >>> ```js >>> class [<identifierName1>] [extends <identifierName2>] [mixes >>> <identifierName3>[, <identifierName3>[, ...]]] { ... } >>> ``` >>> so that the private fields of the objects in the `mixes` list are added >>> to the set of private fields provided by the `class` definition directly. >>> That would also require another proposal, but I think that can be done >>> regardless of which instance-private fields proposal gets accepted. >>> >>> On Sat, Jul 28, 2018 at 12:49 PM Darien Valentine <valentinium at gmail.com> >>> wrote: >>> >>>> To put this another, much briefer way, here’s a hypothetical model for >>>> associating private state with objects that would cover me. Privacy would >>>> be provided... >>>> >>>> 1. in the form of symbolic keys whose presence cannot be observed >>>> (i.e., they would not be exposed by `getOwnPropertySymbols`) >>>> 2. and which have a syntactic declaration so that one can be sure they >>>> are really getting private keys (i.e., an api like `Symbol.private()` >>>> wouldn’t work) >>>> >>>> ``` >>>> const bar = private(); >>>> >>>> // alternatively: const #bar; could be anything so long as it’s >>>> syntactic >>>> >>>> class Foo { >>>> constructor() { >>>> this[bar] = 1; >>>> } >>>> } >>>> >>>> // etc >>>> ``` >>>> >>>> The keys would be typeof 'symbol'; the only difference being that they >>>> are symbols which are flagged as private when created. They would be >>>> permitted only in syntactic property assignments and accesses. Existing >>>> reflection utilities would disallow the use or appearance of such symbols >>>> both to ensure privacy and to maintain the invariant that they are always >>>> simple data properties: >>>> >>>> ```js >>>> Reflect.defineProperty({}, #bar, { ... }); // throws type error >>>> Object.getOwnPropertyDescriptors(someObjWithAPrivateSlot); // does not >>>> include it >>>> foo[bar] = 2; // fine >>>> ``` >>>> >>>> This is significantly simpler than what’s in flight both in terms of >>>> syntax and mechanics, which makes me suspicious that I’m probably ignoring >>>> things that other people find important. However it would bring parity to >>>> ES objects wrt being able to implement genuinely private slots in userland >>>> with the same flexibility as what is done internally. >>>> >>>> In total, this entails a new primary expression, a boolean flag >>>> associated with symbol values, and an extra step added to several >>>> algorithms associated with Object and Reflect. >>>> _______________________________________________ >>>> 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/20180729/0586eddf/attachment-0001.html>
BTW, I came up with an alternate proposal for privacy altogether: tc39/proposal-class-fields#115
TL;DR: private symbols that proxies can't see and that can't be enumerated.
Isiah Meadows me at isiahmeadows.com, www.isiahmeadows.com
BTW, I came up with an alternate proposal for privacy altogether: https://github.com/tc39/proposal-class-fields/issues/115 TL;DR: private symbols that proxies can't see and that can't be enumerated. ----- Isiah Meadows me at isiahmeadows.com www.isiahmeadows.com On Sun, Jul 29, 2018 at 12:23 AM, Darien Valentine <valentinium at gmail.com> wrote: >> What you're essentially asking for is a violatable private field, or as >> has been described by others, a "soft private". > > We might have different definitions here, but I would describe what I’m > talking about as hard private. Soft private, at least as it appears to have > been defined in [prior > discussions](https://github.com/tc39/proposal-private-fields/issues/33), > described an avenue where symbol keyed properties were given a new syntactic > form — but they were still just regular symbol keys, and therefore could be > introspected by outside agents who had not been given express privilege to > do so: > >> [...] the core would be that "private state" is simply (public) >> symbol-named properties, with syntactic sugar for those symbols, and >> possibly some kind of introspection over them [...] > > The thread goes on to contrast the soft model with an earlier version of the > private fields proposal seen today. The hard private example uses the class > declaration as a pseudo-scope, but contrasting these two options as if they > are binary is not accurate: hard private through module/function/block scope > already exists, it is just difficult to work with in the context of shared > prototypes — one must either use WeakMaps, technically giving _hardness_ > because of the forgeability of `global.WeakMap` / `WeakMap.prototype` / > `WeakMap.prototype.get|has|set`, or be willing to either not worry about > garbage collection or implement it manually. This could be solved for with a > few rather undramatic changes, though. > > Notably, the first post there lists the following as a disadvantage of the > soft model it describes: > >> Platform objects, both within ECMAScript and in embedding environments, >> contain hard private state. If a library wants to be high-fidelity and just >> like a platform object, soft-private state does not provide this (@domenic) > > ...but neither model there quite covers that use case. Platform objects > _can_ see each other’s private state (cf the `isView` example earlier, or > scan the DOM API specs / Chrome source a bit to find numerous examples). > It’s only the ES layer interacting with their interfaces that cannot. > > Such things can be achieved with ordinary scope, which is why the WeakMap > pattern has worked in practice in my experience to date, while > class-declaration-scoped privacy has not. It isn’t uncommon for a library’s > exposed interface to be composed of an object graph, where privacy is a > concern at this public interface level, but library internal state may be > interconnected in unexposed ways under the hood. The most familiar example > of this is a DOM node tree. As an experiment, perhaps try to implement the > relationships between HTMLFormElement, HTMLFormControlsCollection and the > various form control elements using either the main private fields proposal > or your alternative proposal and see what happens. > >> However, the guardian logic tries to verify that the function trying to >> access the private fields of an instance is a member of the same or >> descending prototype that was used to create that instance. > > Because I’m looking at this in terms of slots, I’d first point out that > prototypes don’t determine slottedness, the execution of some specific > constructor does. It’s during this process that slots are associated with > the newly minted object by its identity. But even the current private fields > proposal tracks this behavior closely, and I’m not sure how else it could > work. The [[Prototype]] slot of an object is typically mutable > (`R|O.setPrototypeOf`, `__proto__`) and forgeable (Proxy’s `getPrototypeOf` > trap). Why/how would its value matter when it comes to accessing private > state? > > ```js > const pattern = /foo/; > Reflect.setPrototypeOf(pattern, Date.prototype); > pattern instanceof Date; // true > pattern instanceof RegExp; // false > pattern.getMinutes(); // throws TypeError because [[DateValue]] slot is > missing > RegExp.prototype.exec.call(pattern, 'foo'); // works; object has RegExp > private slots > ``` > >> If I removed that requirement, it would work. However, there'd be no way >> to keep the private data from being leaked. Sadly, it's all or nothing with >> this approach. Hard private or soft private, those are the only choices. > > In the context of what you’ve described here this may be true, but no such > limitation presently exists. We can already do all this — hard, leak-free > privacy, brandedness, “friends” etc — with scopes and WeakMaps, but for the > fact that the `WeakMap` intrinsics may be forged. So what’s baffled me is > this: why are all the proposals exploring this space not addressing that > relatively simple existing problem, and instead starting off from a place of > significant new complexity? You said “maybe after the private fields problem > has been resolved, someone will figure out a better way to handle your use > cases,” but I’d have hoped for the opposite — I want the primitive building > blocks which things like class field syntax could be built over, if it is > found that they are still necessary once the root issue is solved for. > >> The main reason the privacy is set on a declaration level is because >> scope-level inheritance isn't very good for class-oriented inheritance. > > Can you explain this more? I’m not sure what’s meant by “scope-level > inheritance” here. > >> I don't intend to stop [...] > > I very much admire your dedication! I’m also digging the discussion. I think > we may be representing viewpoints at opposite extremes here, so it’s an > interesting contrast, but it also probably means we may be lacking some > context for understanding one another’s angles. I’d be curious to hear more > about what you see as the problems with the current fields proposal + how > your members proposal would solve them; the repo readme didn’t seem to > include a rationale section. > > On Sat, Jul 28, 2018 at 10:30 PM Ranando King <kingmph at gmail.com> wrote: >> >> I've almost given up on making any significant headway in either adjusting >> or flat-out correcting the flaws in that proposal, but I don't intend to >> stop trying until either we get stuck with that proposal, or they understand >> and accept what I'm telling them, or logically prove that my concerns are >> either irrational or inconsequential. >> >> > Private object state in particular is only _made complex_ by associating >> > it with declarations instead of scopes that happen to contain declarations >> > (or into which constructors are passed, etc). The complexity is artificial — >> > not a good sign imo. >> >> That's not quite right. What you're essentially asking for is a violatable >> private field, or as has been described by others, a "soft private". Since >> we agree that the "friendly" & "befriend" pair is a somewhat (if not >> completely) bad idea, I'm going to take 1 more pass at your 3 requests with >> a different angle. >> >> > Adding the same “slot” to multiple classes which don’t inherit from each >> > other >> > Selectively sharing access to private state through functions declared >> > outside the class body >> >> ```js >> //Using my proposal >> var {A, B, C} = (() => { >> const common = Symbol("common"); >> >> class A { >> private [common] = 1; >> add(...args) { >> var retval = this#[common]; >> for (let obj of args) { >> retval += obj#[common]; >> } >> return retval; >> } >> } >> class B { >> private [common] = 2; >> optional() { >> console.log(`common member = ${this#[common]}`); >> } >> } >> var C = { >> private [common]: 3, >> required() { >> console.log(`common member = ${this#[common]}`); >> } >> } >> >> return { A, B, C }; >> })(); >> >> //So you want the following statement to not throw a TypeError and return >> 6 >> (new A()).add(new B(), C); >> ``` >> I'm not sure I can make this work in my proposal, and I'm absolutely sure >> you'd be flatly refused by the other proposal. If a `Symbol` is provided as >> the `[[IdentifierName]]` of a private or protected field, then I can let >> that `Symbol` be both the key and value that are added to the >> `[[DeclarationInfo]]` and `[[InheritanceInfo]]` records. That way there will >> be a common private field name usable by all 3 objects. However, the >> guardian logic tries to verify that the function trying to access the >> private fields of an instance is a member of the same or descending >> prototype that was used to create that instance. If I removed that >> requirement, it would work. However, there'd be no way to keep the private >> data from being leaked. Sadly, it's all or nothing with this approach. Hard >> private or soft private, those are the only choices. The TC39 board has >> already decided that what they want new syntax for is hard private. >> >> > Adding slots dynamically, e.g. when adding mix-in methods that may >> > initialize a new slot if necessary when called, since subclassing is not >> > always appropriate >> >> Because the TC39 board has set their sights on hard private, this will >> require new syntax like what I suggested earlier Adding private members >> dynamically would also pose a leak risk if it could be done after the >> prototype has been fully constructed. The main reason the privacy is set on >> a declaration level is because scope-level inheritance isn't very good for >> `class`-oriented inheritance. The `class` keyword was provided to simplify >> the vertical inheritance model, along with some API to enable inheritance >> from native objects even without using `class`. The syntax changes for >> simplifying private field declaration are just an extension of that. Even >> though it's not unusual for some developers to spend a lot of time working >> with fringe use-cases, syntax changes are almost always going to be made for >> the most common use cases first. Maybe after the private fields problem has >> been resolved, someone will figure out a better way to handle your use >> cases. >> >> >> On Sat, Jul 28, 2018 at 3:52 PM Darien Valentine <valentinium at gmail.com> >> wrote: >>> >>> > Are you saying you want multiple non-hierarchally related classes to >>> > have an instance private field with shared name [...] >>> >>> Yeah. This is a hard problem to solve when trying to integrate private >>> fields with class syntax, but it’s not a problem at all when privacy is a >>> more generic tool based on scope. This also isn’t a foreign concept in ES: >>> consider this intrinsic method: >>> >>> https://tc39.github.io/ecma262/#sec-arraybuffer.isview >>> >>> This method returns true if the argument has the `[[ViewedArrayBuffer]]` >>> slot. This slot exists on genuine instances of both `%TypedArray%` and >>> `%DataView%`, but they do not receive these slots by way of inheritance from >>> a common constructor. There are similar cases in HTML host APIs. >>> >>> > The befriend keyword would allow an object to request friendship with >>> > an existing friendly object. I'm not sure this is a good idea, though. >>> >>> I don’t think it is either, no. It’s too much complexity for too little >>> gain. But again, this is achievable “for free” just by divorcing “private >>> object state” from class declarations (or object literals). I would ask: >>> what problem is solved by making this a feature of the declarations >>> themselves? Does it merit the complexity and the hoop jumping needed to >>> handle edge cases?\* >>> >>> \* One person’s edge case; another’s everyday concern haha. >>> >>> > The example you gave above still declares the functions in question >>> > inside the class body, so that's not really a solution. >>> >>> If you’re referring to the first example, that is a demonstration of what >>> is possible using the existing stage 3 class fields proposal as implemented >>> in Chrome. It isn’t what I want; it’s what’s necessary to achieve this with >>> the current stage 3 proposed model. >>> >>> > Sounds to me like you'd love for class syntax to look like this >>> > [[example with mixin syntax in declaration]] >>> >>> Perhaps — it’s interesting for sure! But the pattern that already works, >>> `mixin(Cstr)`, is not presently a source of problems for me. Private object >>> state in particular is only _made complex_ by associating it with >>> declarations instead of scopes that happen to contain declarations (or into >>> which constructors are passed, etc). The complexity is artificial — not a >>> good sign imo. >>> >>> > One thing both proposal-class-fields and proposal-object-members have >>> > in common is that the focus is on producing instance-private fields. All 3 >>> > of the scenarios you presented lay outside of that focus for one reason or >>> > another. >>> >>> Both the WeakMap solution and the stub concept I provided after are more >>> generic than privacy in either of those proposals. When I say "object >>> private state," it’s true that the object in question could be any object. >>> But in practice, any realization of the feature would pertain chiefly to >>> class instances, and the examples I gave, though contrived, do concern class >>> instances. The reason private object state is chiefly an issue of class >>> instances stems directly from the nature of prototype methods and accessors, >>> so if you are not making use of prototypes, you could instead have used a >>> closure+factory directly. >>> >>> --- >>> >>> In a nutshell, my issue with existing proposals could probably be >>> summarized as a concern that they are neither as generic nor as simple as >>> native slots. To be clear, proper “slots” are an internal concept, only >>> observable indirectly — but they are the special sauce underlying a number >>> of behaviors which are presently awkward to achieve in ES code itself, and >>> they are a nice simple model of private object state which is tantalizingly >>> close to, but not _exactly_ the same as in two critical ways, symbol keyed >>> properties. That said, “real” slots would continue to have an advantage with >>> regard to cross-realm stuff even if private symbol keys existed. >>> >>> That such a model is radically simpler — minmax and all that — feels very >>> important to me, but I dunno. I’m not holding my breath for big changes >>> here. The current stage 3 proposal seems to be unstoppable; much smarter / >>> more important people than me have already tried and failed. :) >>> >>> >>> On Sat, Jul 28, 2018 at 3:14 PM Ranando King <kingmph at gmail.com> wrote: >>>> >>>> In a word... wow. You've got me thinking hard here. Those are some >>>> peculiar use cases, and they do a great job of highlighting why someone >>>> might forego using `class`. One thing both proposal-class-fields and >>>> proposal-object-members have in common is that the focus is on producing >>>> instance-private fields. All 3 of the scenarios you presented lay outside of >>>> that focus for one reason or another. >>>> >>>> > Adding the same “slot” to multiple classes which don’t inherit from >>>> > each other >>>> >>>> I'm a little confused by this one. Are you saying you want multiple >>>> non-hierarchally related classes to have an instance private field with >>>> shared name, such that the same private field name refers to a distinct and >>>> separate field on each instance of every such class, but where any such >>>> instance can have that field referenced by that shared name from any member >>>> function of the corresponding classes? (Wow that was wordy to write out...) >>>> If this is what you meant, you're describing friend classes. The top-down >>>> processing nature of ES makes this a difficult thing to create a clean >>>> syntax for without risking leaking the private state or fundamentally >>>> altering how ES is processed. Mutual friendship is even harder. >>>> >>>> ... and yet I just thought of a way to do it. By telling you this I'm >>>> leaving myself to consider writing a proposal containing 2 new keywords: >>>> `befriend` and `friendly`. I don't know if this can be done with the >>>> existing proposal being what it is. However, with my proposal, there's a >>>> chance. The `friendly` keyword would declare that an object is prepared to >>>> share select information with any object that befriends it. The `befriend` >>>> keyword would allow an object to request friendship with an existing >>>> friendly object. I'm not sure this is a good idea, though. This means that >>>> any object declared 'friendly' is automatically insecure as all it takes to >>>> gain access to the selected members of its private space would be to >>>> 'befriend' it. >>>> >>>> > Selectively sharing access to private state through functions declared >>>> > outside the class body >>>> >>>> The example you gave above still declares the functions in question >>>> inside the `class` body, so that's not really a solution. If the example you >>>> gave actually solves your use case, then what you're asking for here isn't >>>> even needed. If, however, that was a bad example, then it sounds like you're >>>> looking for friend functions. See the previous section. >>>> >>>> > Adding slots dynamically, e.g. when adding mix-in methods that may >>>> > initialize a new slot if necessary when called, since subclassing is not >>>> > always appropriate >>>> >>>> Sounds to me like you'd love for `class` syntax to look like this: >>>> >>>> ```js >>>> class [<identifierName1>] [extends <identifierName2>] [mixes >>>> <identifierName3>[, <identifierName3>[, ...]]] { ... } >>>> ``` >>>> so that the private fields of the objects in the `mixes` list are added >>>> to the set of private fields provided by the `class` definition directly. >>>> That would also require another proposal, but I think that can be done >>>> regardless of which instance-private fields proposal gets accepted. >>>> >>>> On Sat, Jul 28, 2018 at 12:49 PM Darien Valentine >>>> <valentinium at gmail.com> wrote: >>>>> >>>>> To put this another, much briefer way, here’s a hypothetical model for >>>>> associating private state with objects that would cover me. Privacy would be >>>>> provided... >>>>> >>>>> 1. in the form of symbolic keys whose presence cannot be observed >>>>> (i.e., they would not be exposed by `getOwnPropertySymbols`) >>>>> 2. and which have a syntactic declaration so that one can be sure they >>>>> are really getting private keys (i.e., an api like `Symbol.private()` >>>>> wouldn’t work) >>>>> >>>>> ``` >>>>> const bar = private(); >>>>> >>>>> // alternatively: const #bar; could be anything so long as it’s >>>>> syntactic >>>>> >>>>> class Foo { >>>>> constructor() { >>>>> this[bar] = 1; >>>>> } >>>>> } >>>>> >>>>> // etc >>>>> ``` >>>>> >>>>> The keys would be typeof 'symbol'; the only difference being that they >>>>> are symbols which are flagged as private when created. They would be >>>>> permitted only in syntactic property assignments and accesses. Existing >>>>> reflection utilities would disallow the use or appearance of such symbols >>>>> both to ensure privacy and to maintain the invariant that they are always >>>>> simple data properties: >>>>> >>>>> ```js >>>>> Reflect.defineProperty({}, #bar, { ... }); // throws type error >>>>> Object.getOwnPropertyDescriptors(someObjWithAPrivateSlot); // does not >>>>> include it >>>>> foo[bar] = 2; // fine >>>>> ``` >>>>> >>>>> This is significantly simpler than what’s in flight both in terms of >>>>> syntax and mechanics, which makes me suspicious that I’m probably ignoring >>>>> things that other people find important. However it would bring parity to ES >>>>> objects wrt being able to implement genuinely private slots in userland with >>>>> the same flexibility as what is done internally. >>>>> >>>>> In total, this entails a new primary expression, a boolean flag >>>>> associated with symbol values, and an extra step added to several algorithms >>>>> associated with Object and Reflect. >>>>> _______________________________________________ >>>>> 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 >
Let me see if I can roll this up: you're looking for syntax support for something akin to Java and C#'s "internal" privilege level, and a means to declare a "common private name" so that something like this will work:
var {A, B, C} = (() => {
static common = Symbol("common");
class A {
internal [common] = 1;
sum(...arg) {
var retval = this#[common];
for (let obj of arg) {
retval += obj#[common];
}
return retval;
}
}
class B {
internal [common] = 2;
sum(...arg) {
var retval = this#[common];
for (let obj of arg) {
retval += obj#[common];
}
return retval;
}
}
class C {
internal [common] = 3;
sum(...arg) {
var retval = this#[common];
for (let obj of arg) {
retval += obj#[common];
}
return retval;
}
}
return { A, B, C };
})();
(new A()).sum(new B(), new C()); //If it works, returns 6
It's already the case that A, B, & C will have to include the
[[DeclarationInfo]]
of the arrow function in each of their respective
methods' __proto__
chains. That and the Symbol
for direct use as a
private name, solves the problem, and avoids the original issue without
much of a change. The "internal" keyword would be trickier to implement as
it would require that the corresponding declarations be added to the
[[DeclarationInfo]]
of the containing scope while the storage exists in
the [[PrivateValues]]
of the owning objects. It's not impossible,
possibly not even difficult. I'll have to give it some thought.
Let me see if I can roll this up: you're looking for syntax support for something akin to Java and C#'s "internal" privilege level, and a means to declare a "common private name" so that something like this will work: ```js var {A, B, C} = (() => { static common = Symbol("common"); class A { internal [common] = 1; sum(...arg) { var retval = this#[common]; for (let obj of arg) { retval += obj#[common]; } return retval; } } class B { internal [common] = 2; sum(...arg) { var retval = this#[common]; for (let obj of arg) { retval += obj#[common]; } return retval; } } class C { internal [common] = 3; sum(...arg) { var retval = this#[common]; for (let obj of arg) { retval += obj#[common]; } return retval; } } return { A, B, C }; })(); (new A()).sum(new B(), new C()); //If it works, returns 6 ``` It's already the case that A, B, & C will have to include the `[[DeclarationInfo]]` of the arrow function in each of their respective methods' `__proto__` chains. That and the `Symbol` for direct use as a private name, solves the problem, and avoids the original issue without much of a change. The "internal" keyword would be trickier to implement as it would require that the corresponding declarations be added to the `[[DeclarationInfo]]` of the containing scope while the storage exists in the `[[PrivateValues]]` of the owning objects. It's not impossible, possibly not even difficult. I'll have to give it some thought. On Sat, Jul 28, 2018 at 11:24 PM Darien Valentine <valentinium at gmail.com> wrote: > > What you're essentially asking for is a violatable private field, or as > has been described by others, a "soft private". > > We might have different definitions here, but I would describe what I’m > talking about as hard private. Soft private, at least as it appears to have > been defined in [prior discussions]( > https://github.com/tc39/proposal-private-fields/issues/33), described an > avenue where symbol keyed properties were given a new syntactic form — but > they were still just regular symbol keys, and therefore could be > introspected by outside agents who had not been given express privilege to > do so: > > > [...] the core would be that "private state" is simply (public) > symbol-named properties, with syntactic sugar for those symbols, and > possibly some kind of introspection over them [...] > > The thread goes on to contrast the soft model with an earlier version of > the private fields proposal seen today. The hard private example uses the > class declaration as a pseudo-scope, but contrasting these two options as > if they are binary is not accurate: hard private through > module/function/block scope already exists, it is just difficult to work > with in the context of shared prototypes — one must either use WeakMaps, > technically giving _hardness_ because of the forgeability of > `global.WeakMap` / `WeakMap.prototype` / `WeakMap.prototype.get|has|set`, > or be willing to either not worry about garbage collection or implement it > manually. This could be solved for with a few rather undramatic changes, > though. > > Notably, the first post there lists the following as a disadvantage of the > soft model it describes: > > > Platform objects, both within ECMAScript and in embedding environments, > contain hard private state. If a library wants to be high-fidelity and just > like a platform object, soft-private state does not provide this (@domenic) > > ...but neither model there quite covers that use case. Platform objects > _can_ see each other’s private state (cf the `isView` example earlier, or > scan the DOM API specs / Chrome source a bit to find numerous examples). > It’s only the ES layer interacting with their interfaces that cannot. > > Such things can be achieved with ordinary scope, which is why the WeakMap > pattern has worked in practice in my experience to date, while > class-declaration-scoped privacy has not. It isn’t uncommon for a library’s > exposed interface to be composed of an object graph, where privacy is a > concern at this public interface level, but library internal state may be > interconnected in unexposed ways under the hood. The most familiar example > of this is a DOM node tree. As an experiment, perhaps try to implement the > relationships between HTMLFormElement, HTMLFormControlsCollection and the > various form control elements using either the main private fields proposal > or your alternative proposal and see what happens. > > > However, the guardian logic tries to verify that the function trying to > access the private fields of an instance is a member of the same or > descending prototype that was used to create that instance. > > Because I’m looking at this in terms of slots, I’d first point out that > prototypes don’t determine slottedness, the execution of some specific > constructor does. It’s during this process that slots are associated with > the newly minted object by its identity. But even the current private > fields proposal tracks this behavior closely, and I’m not sure how else it > could work. The [[Prototype]] slot of an object is typically mutable > (`R|O.setPrototypeOf`, `__proto__`) and forgeable (Proxy’s `getPrototypeOf` > trap). Why/how would its value matter when it comes to accessing private > state? > > ```js > const pattern = /foo/; > Reflect.setPrototypeOf(pattern, Date.prototype); > pattern instanceof Date; // true > pattern instanceof RegExp; // false > pattern.getMinutes(); // throws TypeError because [[DateValue]] slot is > missing > RegExp.prototype.exec.call(pattern, 'foo'); // works; object has RegExp > private slots > ``` > > > If I removed that requirement, it would work. However, there'd be no way > to keep the private data from being leaked. Sadly, it's all or nothing with > this approach. Hard private or soft private, those are the only choices. > > In the context of what you’ve described here this may be true, but no such > limitation presently exists. We can already do all this — hard, leak-free > privacy, brandedness, “friends” etc — with scopes and WeakMaps, but for the > fact that the `WeakMap` intrinsics may be forged. So what’s baffled me is > this: why are all the proposals exploring this space not addressing that > relatively simple existing problem, and instead starting off from a place > of significant new complexity? You said “maybe after the private fields > problem has been resolved, someone will figure out a better way to handle > your use cases,” but I’d have hoped for the opposite — I want the primitive > building blocks which things like class field syntax could be built over, > if it is found that they are still necessary once the root issue is solved > for. > > > The main reason the privacy is set on a declaration level is because > scope-level inheritance isn't very good for class-oriented inheritance. > > Can you explain this more? I’m not sure what’s meant by “scope-level > inheritance” here. > > > I don't intend to stop [...] > > I very much admire your dedication! I’m also digging the discussion. I > think we may be representing viewpoints at opposite extremes here, so it’s > an interesting contrast, but it also probably means we may be lacking some > context for understanding one another’s angles. I’d be curious to hear more > about what you see as the problems with the current fields proposal + how > your members proposal would solve them; the repo readme didn’t seem to > include a rationale section. > > On Sat, Jul 28, 2018 at 10:30 PM Ranando King <kingmph at gmail.com> wrote: > >> I've almost given up on making any significant headway in either >> adjusting or flat-out correcting the flaws in that proposal, but I don't >> intend to stop trying until either we get stuck with that proposal, or they >> understand and accept what I'm telling them, or logically prove that my >> concerns are either irrational or inconsequential. >> >> > Private object state in particular is only _made complex_ by >> associating it with declarations instead of scopes that happen to contain >> declarations (or into which constructors are passed, etc). The complexity >> is artificial — not a good sign imo. >> >> That's not quite right. What you're essentially asking for is a >> violatable private field, or as has been described by others, a "soft >> private". Since we agree that the "friendly" & "befriend" pair is a >> somewhat (if not completely) bad idea, I'm going to take 1 more pass at >> your 3 requests with a different angle. >> >> > Adding the same “slot” to multiple classes which don’t inherit from >> each other >> > Selectively sharing access to private state through functions declared >> outside the class body >> >> ```js >> //Using my proposal >> var {A, B, C} = (() => { >> const common = Symbol("common"); >> >> class A { >> private [common] = 1; >> add(...args) { >> var retval = this#[common]; >> for (let obj of args) { >> retval += obj#[common]; >> } >> return retval; >> } >> } >> class B { >> private [common] = 2; >> optional() { >> console.log(`common member = ${this#[common]}`); >> } >> } >> var C = { >> private [common]: 3, >> required() { >> console.log(`common member = ${this#[common]}`); >> } >> } >> >> return { A, B, C }; >> })(); >> >> //So you want the following statement to not throw a TypeError and return >> 6 >> (new A()).add(new B(), C); >> ``` >> I'm not sure I can make this work in my proposal, and I'm absolutely sure >> you'd be flatly refused by the other proposal. If a `Symbol` is provided as >> the `[[IdentifierName]]` of a private or protected field, then I can let >> that `Symbol` be both the key and value that are added to the >> `[[DeclarationInfo]]` and `[[InheritanceInfo]]` records. That way there >> will be a common private field name usable by all 3 objects. However, the >> guardian logic tries to verify that the function trying to access the >> private fields of an instance is a member of the same or descending >> prototype that was used to create that instance. If I removed that >> requirement, it would work. However, there'd be no way to keep the private >> data from being leaked. Sadly, it's all or nothing with this approach. Hard >> private or soft private, those are the only choices. The TC39 board has >> already decided that what they want new syntax for is hard private. >> >> > Adding slots dynamically, e.g. when adding mix-in methods that may >> initialize a new slot if necessary when called, since subclassing is not >> always appropriate >> >> Because the TC39 board has set their sights on hard private, this will >> require new syntax like what I suggested earlier Adding private members >> dynamically would also pose a leak risk if it could be done after the >> prototype has been fully constructed. The main reason the privacy is set on >> a declaration level is because scope-level inheritance isn't very good for >> `class`-oriented inheritance. The `class` keyword was provided to simplify >> the vertical inheritance model, along with some API to enable inheritance >> from native objects even without using `class`. The syntax changes for >> simplifying private field declaration are just an extension of that. Even >> though it's not unusual for some developers to spend a lot of time working >> with fringe use-cases, syntax changes are almost always going to be made >> for the most common use cases first. Maybe after the private fields problem >> has been resolved, someone will figure out a better way to handle your use >> cases. >> >> >> On Sat, Jul 28, 2018 at 3:52 PM Darien Valentine <valentinium at gmail.com> >> wrote: >> >>> > Are you saying you want multiple non-hierarchally related classes to >>> have an instance private field with shared name [...] >>> >>> Yeah. This is a hard problem to solve when trying to integrate private >>> fields with class syntax, but it’s not a problem at all when privacy is a >>> more generic tool based on scope. This also isn’t a foreign concept in ES: >>> consider this intrinsic method: >>> >>> https://tc39.github.io/ecma262/#sec-arraybuffer.isview >>> >>> This method returns true if the argument has the `[[ViewedArrayBuffer]]` >>> slot. This slot exists on genuine instances of both `%TypedArray%` and >>> `%DataView%`, but they do not receive these slots by way of inheritance >>> from a common constructor. There are similar cases in HTML host APIs. >>> >>> > The befriend keyword would allow an object to request friendship with >>> an existing friendly object. I'm not sure this is a good idea, though. >>> >>> I don’t think it is either, no. It’s too much complexity for too little >>> gain. But again, this is achievable “for free” just by divorcing “private >>> object state” from class declarations (or object literals). I would ask: >>> what problem is solved by making this a feature of the declarations >>> themselves? Does it merit the complexity and the hoop jumping needed to >>> handle edge cases?\* >>> >>> \* One person’s edge case; another’s everyday concern haha. >>> >>> > The example you gave above still declares the functions in question >>> inside the class body, so that's not really a solution. >>> >>> If you’re referring to the first example, that is a demonstration of >>> what is possible using the existing stage 3 class fields proposal as >>> implemented in Chrome. It isn’t what I want; it’s what’s necessary to >>> achieve this with the current stage 3 proposed model. >>> >>> > Sounds to me like you'd love for class syntax to look like this >>> [[example with mixin syntax in declaration]] >>> >>> Perhaps — it’s interesting for sure! But the pattern that already works, >>> `mixin(Cstr)`, is not presently a source of problems for me. Private object >>> state in particular is only _made complex_ by associating it with >>> declarations instead of scopes that happen to contain declarations (or into >>> which constructors are passed, etc). The complexity is artificial — not a >>> good sign imo. >>> >>> > One thing both proposal-class-fields and proposal-object-members have >>> in common is that the focus is on producing instance-private fields. All 3 >>> of the scenarios you presented lay outside of that focus for one reason or >>> another. >>> >>> Both the WeakMap solution and the stub concept I provided after are more >>> generic than privacy in either of those proposals. When I say "object >>> private state," it’s true that the object in question could be any object. >>> But in practice, any realization of the feature would pertain chiefly to >>> class instances, and the examples I gave, though contrived, do concern >>> class instances. The reason private object state is chiefly an issue of >>> class instances stems directly from the nature of prototype methods and >>> accessors, so if you are not making use of prototypes, you could instead >>> have used a closure+factory directly. >>> >>> --- >>> >>> In a nutshell, my issue with existing proposals could probably be >>> summarized as a concern that they are neither as generic nor as simple as >>> native slots. To be clear, proper “slots” are an internal concept, only >>> observable indirectly — but they are the special sauce underlying a number >>> of behaviors which are presently awkward to achieve in ES code itself, and >>> they are a nice simple model of private object state which is tantalizingly >>> close to, but not _exactly_ the same as in two critical ways, symbol keyed >>> properties. That said, “real” slots would continue to have an advantage >>> with regard to cross-realm stuff even if private symbol keys existed. >>> >>> That such a model is radically simpler — minmax and all that — feels >>> very important to me, but I dunno. I’m not holding my breath for big >>> changes here. The current stage 3 proposal seems to be unstoppable; much >>> smarter / more important people than me have already tried and failed. :) >>> >>> >>> On Sat, Jul 28, 2018 at 3:14 PM Ranando King <kingmph at gmail.com> wrote: >>> >>>> In a word... wow. You've got me thinking hard here. Those are some >>>> peculiar use cases, and they do a great job of highlighting why someone >>>> might forego using `class`. One thing both proposal-class-fields and >>>> proposal-object-members have in common is that the focus is on producing >>>> instance-private fields. All 3 of the scenarios you presented lay outside >>>> of that focus for one reason or another. >>>> >>>> > Adding the same “slot” to multiple classes which don’t inherit from >>>> each other >>>> >>>> I'm a little confused by this one. Are you saying you want multiple >>>> non-hierarchally related classes to have an instance private field with >>>> shared name, such that the same private field name refers to a distinct and >>>> separate field on each instance of every such class, but where any such >>>> instance can have that field referenced by that shared name from any member >>>> function of the corresponding classes? (Wow that was wordy to write out...) >>>> If this is what you meant, you're describing friend classes. The top-down >>>> processing nature of ES makes this a difficult thing to create a clean >>>> syntax for without risking leaking the private state or fundamentally >>>> altering how ES is processed. Mutual friendship is even harder. >>>> >>>> ... and yet I just thought of a way to do it. By telling you this I'm >>>> leaving myself to consider writing a proposal containing 2 new keywords: >>>> `befriend` and `friendly`. I don't know if this can be done with the >>>> existing proposal being what it is. However, with my proposal, there's a >>>> chance. The `friendly` keyword would declare that an object is prepared to >>>> share select information with any object that befriends it. The `befriend` >>>> keyword would allow an object to request friendship with an existing >>>> friendly object. I'm not sure this is a good idea, though. This means that >>>> any object declared 'friendly' is automatically insecure as all it takes to >>>> gain access to the selected members of its private space would be to >>>> 'befriend' it. >>>> >>>> > Selectively sharing access to private state through functions >>>> declared outside the class body >>>> >>>> The example you gave above still declares the functions in question >>>> inside the `class` body, so that's not really a solution. If the example >>>> you gave actually solves your use case, then what you're asking for here >>>> isn't even needed. If, however, that was a bad example, then it sounds like >>>> you're looking for friend functions. See the previous section. >>>> >>>> > Adding slots dynamically, e.g. when adding mix-in methods that may >>>> initialize a new slot if necessary when called, since subclassing is not >>>> always appropriate >>>> >>>> Sounds to me like you'd love for `class` syntax to look like this: >>>> >>>> ```js >>>> class [<identifierName1>] [extends <identifierName2>] [mixes >>>> <identifierName3>[, <identifierName3>[, ...]]] { ... } >>>> ``` >>>> so that the private fields of the objects in the `mixes` list are added >>>> to the set of private fields provided by the `class` definition directly. >>>> That would also require another proposal, but I think that can be done >>>> regardless of which instance-private fields proposal gets accepted. >>>> >>>> On Sat, Jul 28, 2018 at 12:49 PM Darien Valentine < >>>> valentinium at gmail.com> wrote: >>>> >>>>> To put this another, much briefer way, here’s a hypothetical model for >>>>> associating private state with objects that would cover me. Privacy would >>>>> be provided... >>>>> >>>>> 1. in the form of symbolic keys whose presence cannot be observed >>>>> (i.e., they would not be exposed by `getOwnPropertySymbols`) >>>>> 2. and which have a syntactic declaration so that one can be sure they >>>>> are really getting private keys (i.e., an api like `Symbol.private()` >>>>> wouldn’t work) >>>>> >>>>> ``` >>>>> const bar = private(); >>>>> >>>>> // alternatively: const #bar; could be anything so long as it’s >>>>> syntactic >>>>> >>>>> class Foo { >>>>> constructor() { >>>>> this[bar] = 1; >>>>> } >>>>> } >>>>> >>>>> // etc >>>>> ``` >>>>> >>>>> The keys would be typeof 'symbol'; the only difference being that they >>>>> are symbols which are flagged as private when created. They would be >>>>> permitted only in syntactic property assignments and accesses. Existing >>>>> reflection utilities would disallow the use or appearance of such symbols >>>>> both to ensure privacy and to maintain the invariant that they are always >>>>> simple data properties: >>>>> >>>>> ```js >>>>> Reflect.defineProperty({}, #bar, { ... }); // throws type error >>>>> Object.getOwnPropertyDescriptors(someObjWithAPrivateSlot); // does not >>>>> include it >>>>> foo[bar] = 2; // fine >>>>> ``` >>>>> >>>>> This is significantly simpler than what’s in flight both in terms of >>>>> syntax and mechanics, which makes me suspicious that I’m probably ignoring >>>>> things that other people find important. However it would bring parity to >>>>> ES objects wrt being able to implement genuinely private slots in userland >>>>> with the same flexibility as what is done internally. >>>>> >>>>> In total, this entails a new primary expression, a boolean flag >>>>> associated with symbol values, and an extra step added to several >>>>> algorithms associated with Object and Reflect. >>>>> _______________________________________________ >>>>> 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/20180729/b9446bba/attachment-0001.html>
Isaiah, that’s pretty similar to what I was talking about earlier — and I think it’s awesome that multiple people have arrived there, since it seems like soft confirmation of the idea that private symbols likely represent the most minimal possible “surgery” to achieve the functionality.
There are some differences; I saw Symbol.private
as an obvious API too,
but specifically mentioned it to point out that it’s not viable if we
consider one of the goals here to be the elimination of the
“tamperability-hole” that exists for any non-syntactic solution (unless
global.Symbol is redefined as non-configurable, non-writable, etc, but this
is likely not an option).
The proxy forwarding is very interesting, but the prototype lookup doesn’t make sense to me. Slots/fields are associated with an object, not subject to the vagaries of that object’s present prototype chain. I don’t think changing that relationship is a good idea.
Isaiah, that’s pretty similar to what I was [talking about earlier]( https://mail.mozilla.org/pipermail/es-discuss/2018-July/051410.html) — and I think it’s awesome that multiple people have arrived there, since it seems like soft confirmation of the idea that private symbols likely represent the most minimal possible “surgery” to achieve the functionality. There are some differences; I saw `Symbol.private` as an obvious API too, but specifically mentioned it to point out that it’s not viable if we consider one of the goals here to be the elimination of the “tamperability-hole” that exists for any non-syntactic solution (unless global.Symbol is redefined as non-configurable, non-writable, etc, but this is likely not an option). The proxy forwarding is _very_ interesting, but the prototype lookup doesn’t make sense to me. Slots/fields are associated with _an_ object, not subject to the vagaries of that object’s present prototype chain. I don’t think changing that relationship is a good idea. On Sun, Jul 29, 2018 at 7:38 PM Isiah Meadows <isiahmeadows at gmail.com> wrote: > BTW, I came up with an alternate proposal for privacy altogether: > https://github.com/tc39/proposal-class-fields/issues/115 > > TL;DR: private symbols that proxies can't see and that can't be enumerated. > ----- > > Isiah Meadows > me at isiahmeadows.com > www.isiahmeadows.com > > > On Sun, Jul 29, 2018 at 12:23 AM, Darien Valentine > <valentinium at gmail.com> wrote: > >> What you're essentially asking for is a violatable private field, or as > >> has been described by others, a "soft private". > > > > We might have different definitions here, but I would describe what I’m > > talking about as hard private. Soft private, at least as it appears to > have > > been defined in [prior > > discussions](https://github.com/tc39/proposal-private-fields/issues/33), > > described an avenue where symbol keyed properties were given a new > syntactic > > form — but they were still just regular symbol keys, and therefore could > be > > introspected by outside agents who had not been given express privilege > to > > do so: > > > >> [...] the core would be that "private state" is simply (public) > >> symbol-named properties, with syntactic sugar for those symbols, and > >> possibly some kind of introspection over them [...] > > > > The thread goes on to contrast the soft model with an earlier version of > the > > private fields proposal seen today. The hard private example uses the > class > > declaration as a pseudo-scope, but contrasting these two options as if > they > > are binary is not accurate: hard private through module/function/block > scope > > already exists, it is just difficult to work with in the context of > shared > > prototypes — one must either use WeakMaps, technically giving _hardness_ > > because of the forgeability of `global.WeakMap` / `WeakMap.prototype` / > > `WeakMap.prototype.get|has|set`, or be willing to either not worry about > > garbage collection or implement it manually. This could be solved for > with a > > few rather undramatic changes, though. > > > > Notably, the first post there lists the following as a disadvantage of > the > > soft model it describes: > > > >> Platform objects, both within ECMAScript and in embedding environments, > >> contain hard private state. If a library wants to be high-fidelity and > just > >> like a platform object, soft-private state does not provide this > (@domenic) > > > > ...but neither model there quite covers that use case. Platform objects > > _can_ see each other’s private state (cf the `isView` example earlier, or > > scan the DOM API specs / Chrome source a bit to find numerous examples). > > It’s only the ES layer interacting with their interfaces that cannot. > > > > Such things can be achieved with ordinary scope, which is why the WeakMap > > pattern has worked in practice in my experience to date, while > > class-declaration-scoped privacy has not. It isn’t uncommon for a > library’s > > exposed interface to be composed of an object graph, where privacy is a > > concern at this public interface level, but library internal state may be > > interconnected in unexposed ways under the hood. The most familiar > example > > of this is a DOM node tree. As an experiment, perhaps try to implement > the > > relationships between HTMLFormElement, HTMLFormControlsCollection and the > > various form control elements using either the main private fields > proposal > > or your alternative proposal and see what happens. > > > >> However, the guardian logic tries to verify that the function trying to > >> access the private fields of an instance is a member of the same or > >> descending prototype that was used to create that instance. > > > > Because I’m looking at this in terms of slots, I’d first point out that > > prototypes don’t determine slottedness, the execution of some specific > > constructor does. It’s during this process that slots are associated with > > the newly minted object by its identity. But even the current private > fields > > proposal tracks this behavior closely, and I’m not sure how else it could > > work. The [[Prototype]] slot of an object is typically mutable > > (`R|O.setPrototypeOf`, `__proto__`) and forgeable (Proxy’s > `getPrototypeOf` > > trap). Why/how would its value matter when it comes to accessing private > > state? > > > > ```js > > const pattern = /foo/; > > Reflect.setPrototypeOf(pattern, Date.prototype); > > pattern instanceof Date; // true > > pattern instanceof RegExp; // false > > pattern.getMinutes(); // throws TypeError because [[DateValue]] slot is > > missing > > RegExp.prototype.exec.call(pattern, 'foo'); // works; object has RegExp > > private slots > > ``` > > > >> If I removed that requirement, it would work. However, there'd be no way > >> to keep the private data from being leaked. Sadly, it's all or nothing > with > >> this approach. Hard private or soft private, those are the only choices. > > > > In the context of what you’ve described here this may be true, but no > such > > limitation presently exists. We can already do all this — hard, leak-free > > privacy, brandedness, “friends” etc — with scopes and WeakMaps, but for > the > > fact that the `WeakMap` intrinsics may be forged. So what’s baffled me is > > this: why are all the proposals exploring this space not addressing that > > relatively simple existing problem, and instead starting off from a > place of > > significant new complexity? You said “maybe after the private fields > problem > > has been resolved, someone will figure out a better way to handle your > use > > cases,” but I’d have hoped for the opposite — I want the primitive > building > > blocks which things like class field syntax could be built over, if it is > > found that they are still necessary once the root issue is solved for. > > > >> The main reason the privacy is set on a declaration level is because > >> scope-level inheritance isn't very good for class-oriented inheritance. > > > > Can you explain this more? I’m not sure what’s meant by “scope-level > > inheritance” here. > > > >> I don't intend to stop [...] > > > > I very much admire your dedication! I’m also digging the discussion. I > think > > we may be representing viewpoints at opposite extremes here, so it’s an > > interesting contrast, but it also probably means we may be lacking some > > context for understanding one another’s angles. I’d be curious to hear > more > > about what you see as the problems with the current fields proposal + how > > your members proposal would solve them; the repo readme didn’t seem to > > include a rationale section. > > > > On Sat, Jul 28, 2018 at 10:30 PM Ranando King <kingmph at gmail.com> wrote: > >> > >> I've almost given up on making any significant headway in either > adjusting > >> or flat-out correcting the flaws in that proposal, but I don't intend to > >> stop trying until either we get stuck with that proposal, or they > understand > >> and accept what I'm telling them, or logically prove that my concerns > are > >> either irrational or inconsequential. > >> > >> > Private object state in particular is only _made complex_ by > associating > >> > it with declarations instead of scopes that happen to contain > declarations > >> > (or into which constructors are passed, etc). The complexity is > artificial — > >> > not a good sign imo. > >> > >> That's not quite right. What you're essentially asking for is a > violatable > >> private field, or as has been described by others, a "soft private". > Since > >> we agree that the "friendly" & "befriend" pair is a somewhat (if not > >> completely) bad idea, I'm going to take 1 more pass at your 3 requests > with > >> a different angle. > >> > >> > Adding the same “slot” to multiple classes which don’t inherit from > each > >> > other > >> > Selectively sharing access to private state through functions declared > >> > outside the class body > >> > >> ```js > >> //Using my proposal > >> var {A, B, C} = (() => { > >> const common = Symbol("common"); > >> > >> class A { > >> private [common] = 1; > >> add(...args) { > >> var retval = this#[common]; > >> for (let obj of args) { > >> retval += obj#[common]; > >> } > >> return retval; > >> } > >> } > >> class B { > >> private [common] = 2; > >> optional() { > >> console.log(`common member = ${this#[common]}`); > >> } > >> } > >> var C = { > >> private [common]: 3, > >> required() { > >> console.log(`common member = ${this#[common]}`); > >> } > >> } > >> > >> return { A, B, C }; > >> })(); > >> > >> //So you want the following statement to not throw a TypeError and > return > >> 6 > >> (new A()).add(new B(), C); > >> ``` > >> I'm not sure I can make this work in my proposal, and I'm absolutely > sure > >> you'd be flatly refused by the other proposal. If a `Symbol` is > provided as > >> the `[[IdentifierName]]` of a private or protected field, then I can let > >> that `Symbol` be both the key and value that are added to the > >> `[[DeclarationInfo]]` and `[[InheritanceInfo]]` records. That way there > will > >> be a common private field name usable by all 3 objects. However, the > >> guardian logic tries to verify that the function trying to access the > >> private fields of an instance is a member of the same or descending > >> prototype that was used to create that instance. If I removed that > >> requirement, it would work. However, there'd be no way to keep the > private > >> data from being leaked. Sadly, it's all or nothing with this approach. > Hard > >> private or soft private, those are the only choices. The TC39 board has > >> already decided that what they want new syntax for is hard private. > >> > >> > Adding slots dynamically, e.g. when adding mix-in methods that may > >> > initialize a new slot if necessary when called, since subclassing is > not > >> > always appropriate > >> > >> Because the TC39 board has set their sights on hard private, this will > >> require new syntax like what I suggested earlier Adding private members > >> dynamically would also pose a leak risk if it could be done after the > >> prototype has been fully constructed. The main reason the privacy is > set on > >> a declaration level is because scope-level inheritance isn't very good > for > >> `class`-oriented inheritance. The `class` keyword was provided to > simplify > >> the vertical inheritance model, along with some API to enable > inheritance > >> from native objects even without using `class`. The syntax changes for > >> simplifying private field declaration are just an extension of that. > Even > >> though it's not unusual for some developers to spend a lot of time > working > >> with fringe use-cases, syntax changes are almost always going to be > made for > >> the most common use cases first. Maybe after the private fields problem > has > >> been resolved, someone will figure out a better way to handle your use > >> cases. > >> > >> > >> On Sat, Jul 28, 2018 at 3:52 PM Darien Valentine <valentinium at gmail.com > > > >> wrote: > >>> > >>> > Are you saying you want multiple non-hierarchally related classes to > >>> > have an instance private field with shared name [...] > >>> > >>> Yeah. This is a hard problem to solve when trying to integrate private > >>> fields with class syntax, but it’s not a problem at all when privacy > is a > >>> more generic tool based on scope. This also isn’t a foreign concept in > ES: > >>> consider this intrinsic method: > >>> > >>> https://tc39.github.io/ecma262/#sec-arraybuffer.isview > >>> > >>> This method returns true if the argument has the > `[[ViewedArrayBuffer]]` > >>> slot. This slot exists on genuine instances of both `%TypedArray%` and > >>> `%DataView%`, but they do not receive these slots by way of > inheritance from > >>> a common constructor. There are similar cases in HTML host APIs. > >>> > >>> > The befriend keyword would allow an object to request friendship with > >>> > an existing friendly object. I'm not sure this is a good idea, > though. > >>> > >>> I don’t think it is either, no. It’s too much complexity for too little > >>> gain. But again, this is achievable “for free” just by divorcing > “private > >>> object state” from class declarations (or object literals). I would > ask: > >>> what problem is solved by making this a feature of the declarations > >>> themselves? Does it merit the complexity and the hoop jumping needed to > >>> handle edge cases?\* > >>> > >>> \* One person’s edge case; another’s everyday concern haha. > >>> > >>> > The example you gave above still declares the functions in question > >>> > inside the class body, so that's not really a solution. > >>> > >>> If you’re referring to the first example, that is a demonstration of > what > >>> is possible using the existing stage 3 class fields proposal as > implemented > >>> in Chrome. It isn’t what I want; it’s what’s necessary to achieve this > with > >>> the current stage 3 proposed model. > >>> > >>> > Sounds to me like you'd love for class syntax to look like this > >>> > [[example with mixin syntax in declaration]] > >>> > >>> Perhaps — it’s interesting for sure! But the pattern that already > works, > >>> `mixin(Cstr)`, is not presently a source of problems for me. Private > object > >>> state in particular is only _made complex_ by associating it with > >>> declarations instead of scopes that happen to contain declarations (or > into > >>> which constructors are passed, etc). The complexity is artificial — > not a > >>> good sign imo. > >>> > >>> > One thing both proposal-class-fields and proposal-object-members > have > >>> > in common is that the focus is on producing instance-private fields. > All 3 > >>> > of the scenarios you presented lay outside of that focus for one > reason or > >>> > another. > >>> > >>> Both the WeakMap solution and the stub concept I provided after are > more > >>> generic than privacy in either of those proposals. When I say "object > >>> private state," it’s true that the object in question could be any > object. > >>> But in practice, any realization of the feature would pertain chiefly > to > >>> class instances, and the examples I gave, though contrived, do concern > class > >>> instances. The reason private object state is chiefly an issue of class > >>> instances stems directly from the nature of prototype methods and > accessors, > >>> so if you are not making use of prototypes, you could instead have > used a > >>> closure+factory directly. > >>> > >>> --- > >>> > >>> In a nutshell, my issue with existing proposals could probably be > >>> summarized as a concern that they are neither as generic nor as simple > as > >>> native slots. To be clear, proper “slots” are an internal concept, only > >>> observable indirectly — but they are the special sauce underlying a > number > >>> of behaviors which are presently awkward to achieve in ES code itself, > and > >>> they are a nice simple model of private object state which is > tantalizingly > >>> close to, but not _exactly_ the same as in two critical ways, symbol > keyed > >>> properties. That said, “real” slots would continue to have an > advantage with > >>> regard to cross-realm stuff even if private symbol keys existed. > >>> > >>> That such a model is radically simpler — minmax and all that — feels > very > >>> important to me, but I dunno. I’m not holding my breath for big changes > >>> here. The current stage 3 proposal seems to be unstoppable; much > smarter / > >>> more important people than me have already tried and failed. :) > >>> > >>> > >>> On Sat, Jul 28, 2018 at 3:14 PM Ranando King <kingmph at gmail.com> > wrote: > >>>> > >>>> In a word... wow. You've got me thinking hard here. Those are some > >>>> peculiar use cases, and they do a great job of highlighting why > someone > >>>> might forego using `class`. One thing both proposal-class-fields and > >>>> proposal-object-members have in common is that the focus is on > producing > >>>> instance-private fields. All 3 of the scenarios you presented lay > outside of > >>>> that focus for one reason or another. > >>>> > >>>> > Adding the same “slot” to multiple classes which don’t inherit from > >>>> > each other > >>>> > >>>> I'm a little confused by this one. Are you saying you want multiple > >>>> non-hierarchally related classes to have an instance private field > with > >>>> shared name, such that the same private field name refers to a > distinct and > >>>> separate field on each instance of every such class, but where any > such > >>>> instance can have that field referenced by that shared name from any > member > >>>> function of the corresponding classes? (Wow that was wordy to write > out...) > >>>> If this is what you meant, you're describing friend classes. The > top-down > >>>> processing nature of ES makes this a difficult thing to create a clean > >>>> syntax for without risking leaking the private state or fundamentally > >>>> altering how ES is processed. Mutual friendship is even harder. > >>>> > >>>> ... and yet I just thought of a way to do it. By telling you this I'm > >>>> leaving myself to consider writing a proposal containing 2 new > keywords: > >>>> `befriend` and `friendly`. I don't know if this can be done with the > >>>> existing proposal being what it is. However, with my proposal, > there's a > >>>> chance. The `friendly` keyword would declare that an object is > prepared to > >>>> share select information with any object that befriends it. The > `befriend` > >>>> keyword would allow an object to request friendship with an existing > >>>> friendly object. I'm not sure this is a good idea, though. This means > that > >>>> any object declared 'friendly' is automatically insecure as all it > takes to > >>>> gain access to the selected members of its private space would be to > >>>> 'befriend' it. > >>>> > >>>> > Selectively sharing access to private state through functions > declared > >>>> > outside the class body > >>>> > >>>> The example you gave above still declares the functions in question > >>>> inside the `class` body, so that's not really a solution. If the > example you > >>>> gave actually solves your use case, then what you're asking for here > isn't > >>>> even needed. If, however, that was a bad example, then it sounds like > you're > >>>> looking for friend functions. See the previous section. > >>>> > >>>> > Adding slots dynamically, e.g. when adding mix-in methods that may > >>>> > initialize a new slot if necessary when called, since subclassing > is not > >>>> > always appropriate > >>>> > >>>> Sounds to me like you'd love for `class` syntax to look like this: > >>>> > >>>> ```js > >>>> class [<identifierName1>] [extends <identifierName2>] [mixes > >>>> <identifierName3>[, <identifierName3>[, ...]]] { ... } > >>>> ``` > >>>> so that the private fields of the objects in the `mixes` list are > added > >>>> to the set of private fields provided by the `class` definition > directly. > >>>> That would also require another proposal, but I think that can be done > >>>> regardless of which instance-private fields proposal gets accepted. > >>>> > >>>> On Sat, Jul 28, 2018 at 12:49 PM Darien Valentine > >>>> <valentinium at gmail.com> wrote: > >>>>> > >>>>> To put this another, much briefer way, here’s a hypothetical model > for > >>>>> associating private state with objects that would cover me. Privacy > would be > >>>>> provided... > >>>>> > >>>>> 1. in the form of symbolic keys whose presence cannot be observed > >>>>> (i.e., they would not be exposed by `getOwnPropertySymbols`) > >>>>> 2. and which have a syntactic declaration so that one can be sure > they > >>>>> are really getting private keys (i.e., an api like `Symbol.private()` > >>>>> wouldn’t work) > >>>>> > >>>>> ``` > >>>>> const bar = private(); > >>>>> > >>>>> // alternatively: const #bar; could be anything so long as it’s > >>>>> syntactic > >>>>> > >>>>> class Foo { > >>>>> constructor() { > >>>>> this[bar] = 1; > >>>>> } > >>>>> } > >>>>> > >>>>> // etc > >>>>> ``` > >>>>> > >>>>> The keys would be typeof 'symbol'; the only difference being that > they > >>>>> are symbols which are flagged as private when created. They would be > >>>>> permitted only in syntactic property assignments and accesses. > Existing > >>>>> reflection utilities would disallow the use or appearance of such > symbols > >>>>> both to ensure privacy and to maintain the invariant that they are > always > >>>>> simple data properties: > >>>>> > >>>>> ```js > >>>>> Reflect.defineProperty({}, #bar, { ... }); // throws type error > >>>>> Object.getOwnPropertyDescriptors(someObjWithAPrivateSlot); // does > not > >>>>> include it > >>>>> foo[bar] = 2; // fine > >>>>> ``` > >>>>> > >>>>> This is significantly simpler than what’s in flight both in terms of > >>>>> syntax and mechanics, which makes me suspicious that I’m probably > ignoring > >>>>> things that other people find important. However it would bring > parity to ES > >>>>> objects wrt being able to implement genuinely private slots in > userland with > >>>>> the same flexibility as what is done internally. > >>>>> > >>>>> In total, this entails a new primary expression, a boolean flag > >>>>> associated with symbol values, and an extra step added to several > algorithms > >>>>> associated with Object and Reflect. > >>>>> _______________________________________________ > >>>>> 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/20180729/1e51589f/attachment-0001.html>
- I don't believe tampering is a major issue, especially considering most tampering problems occur after the type is instantiated. If it is, the follow-on proposal of added syntax would likely avoid it.
- The prototype part is just for consistency and code reuse. And it's not about the object it's associated, but about the key lookup itself.
Isiah Meadows me at isiahmeadows.com, www.isiahmeadows.com
1. I don't believe tampering is a major issue, especially considering most tampering problems occur *after* the type is instantiated. If it is, the follow-on proposal of added syntax would likely avoid it. 2. The prototype part is just for consistency and code reuse. And it's not about the object it's associated, but about the key lookup itself. ----- Isiah Meadows me at isiahmeadows.com www.isiahmeadows.com On Sun, Jul 29, 2018 at 8:00 PM, Darien Valentine <valentinium at gmail.com> wrote: > Isaiah, that’s pretty similar to what I was [talking about > earlier](https://mail.mozilla.org/pipermail/es-discuss/2018-July/051410.html) > — and I think it’s awesome that multiple people have arrived there, since it > seems like soft confirmation of the idea that private symbols likely > represent the most minimal possible “surgery” to achieve the functionality. > > There are some differences; I saw `Symbol.private` as an obvious API too, > but specifically mentioned it to point out that it’s not viable if we > consider one of the goals here to be the elimination of the > “tamperability-hole” that exists for any non-syntactic solution (unless > global.Symbol is redefined as non-configurable, non-writable, etc, but this > is likely not an option). > > The proxy forwarding is _very_ interesting, but the prototype lookup doesn’t > make sense to me. Slots/fields are associated with _an_ object, not subject > to the vagaries of that object’s present prototype chain. I don’t think > changing that relationship is a good idea. > > On Sun, Jul 29, 2018 at 7:38 PM Isiah Meadows <isiahmeadows at gmail.com> > wrote: >> >> BTW, I came up with an alternate proposal for privacy altogether: >> https://github.com/tc39/proposal-class-fields/issues/115 >> >> TL;DR: private symbols that proxies can't see and that can't be >> enumerated. >> ----- >> >> Isiah Meadows >> me at isiahmeadows.com >> www.isiahmeadows.com >> >> >> On Sun, Jul 29, 2018 at 12:23 AM, Darien Valentine >> <valentinium at gmail.com> wrote: >> >> What you're essentially asking for is a violatable private field, or as >> >> has been described by others, a "soft private". >> > >> > We might have different definitions here, but I would describe what I’m >> > talking about as hard private. Soft private, at least as it appears to >> > have >> > been defined in [prior >> > discussions](https://github.com/tc39/proposal-private-fields/issues/33), >> > described an avenue where symbol keyed properties were given a new >> > syntactic >> > form — but they were still just regular symbol keys, and therefore could >> > be >> > introspected by outside agents who had not been given express privilege >> > to >> > do so: >> > >> >> [...] the core would be that "private state" is simply (public) >> >> symbol-named properties, with syntactic sugar for those symbols, and >> >> possibly some kind of introspection over them [...] >> > >> > The thread goes on to contrast the soft model with an earlier version of >> > the >> > private fields proposal seen today. The hard private example uses the >> > class >> > declaration as a pseudo-scope, but contrasting these two options as if >> > they >> > are binary is not accurate: hard private through module/function/block >> > scope >> > already exists, it is just difficult to work with in the context of >> > shared >> > prototypes — one must either use WeakMaps, technically giving _hardness_ >> > because of the forgeability of `global.WeakMap` / `WeakMap.prototype` / >> > `WeakMap.prototype.get|has|set`, or be willing to either not worry about >> > garbage collection or implement it manually. This could be solved for >> > with a >> > few rather undramatic changes, though. >> > >> > Notably, the first post there lists the following as a disadvantage of >> > the >> > soft model it describes: >> > >> >> Platform objects, both within ECMAScript and in embedding environments, >> >> contain hard private state. If a library wants to be high-fidelity and >> >> just >> >> like a platform object, soft-private state does not provide this >> >> (@domenic) >> > >> > ...but neither model there quite covers that use case. Platform objects >> > _can_ see each other’s private state (cf the `isView` example earlier, >> > or >> > scan the DOM API specs / Chrome source a bit to find numerous examples). >> > It’s only the ES layer interacting with their interfaces that cannot. >> > >> > Such things can be achieved with ordinary scope, which is why the >> > WeakMap >> > pattern has worked in practice in my experience to date, while >> > class-declaration-scoped privacy has not. It isn’t uncommon for a >> > library’s >> > exposed interface to be composed of an object graph, where privacy is a >> > concern at this public interface level, but library internal state may >> > be >> > interconnected in unexposed ways under the hood. The most familiar >> > example >> > of this is a DOM node tree. As an experiment, perhaps try to implement >> > the >> > relationships between HTMLFormElement, HTMLFormControlsCollection and >> > the >> > various form control elements using either the main private fields >> > proposal >> > or your alternative proposal and see what happens. >> > >> >> However, the guardian logic tries to verify that the function trying to >> >> access the private fields of an instance is a member of the same or >> >> descending prototype that was used to create that instance. >> > >> > Because I’m looking at this in terms of slots, I’d first point out that >> > prototypes don’t determine slottedness, the execution of some specific >> > constructor does. It’s during this process that slots are associated >> > with >> > the newly minted object by its identity. But even the current private >> > fields >> > proposal tracks this behavior closely, and I’m not sure how else it >> > could >> > work. The [[Prototype]] slot of an object is typically mutable >> > (`R|O.setPrototypeOf`, `__proto__`) and forgeable (Proxy’s >> > `getPrototypeOf` >> > trap). Why/how would its value matter when it comes to accessing private >> > state? >> > >> > ```js >> > const pattern = /foo/; >> > Reflect.setPrototypeOf(pattern, Date.prototype); >> > pattern instanceof Date; // true >> > pattern instanceof RegExp; // false >> > pattern.getMinutes(); // throws TypeError because [[DateValue]] slot is >> > missing >> > RegExp.prototype.exec.call(pattern, 'foo'); // works; object has RegExp >> > private slots >> > ``` >> > >> >> If I removed that requirement, it would work. However, there'd be no >> >> way >> >> to keep the private data from being leaked. Sadly, it's all or nothing >> >> with >> >> this approach. Hard private or soft private, those are the only >> >> choices. >> > >> > In the context of what you’ve described here this may be true, but no >> > such >> > limitation presently exists. We can already do all this — hard, >> > leak-free >> > privacy, brandedness, “friends” etc — with scopes and WeakMaps, but for >> > the >> > fact that the `WeakMap` intrinsics may be forged. So what’s baffled me >> > is >> > this: why are all the proposals exploring this space not addressing that >> > relatively simple existing problem, and instead starting off from a >> > place of >> > significant new complexity? You said “maybe after the private fields >> > problem >> > has been resolved, someone will figure out a better way to handle your >> > use >> > cases,” but I’d have hoped for the opposite — I want the primitive >> > building >> > blocks which things like class field syntax could be built over, if it >> > is >> > found that they are still necessary once the root issue is solved for. >> > >> >> The main reason the privacy is set on a declaration level is because >> >> scope-level inheritance isn't very good for class-oriented inheritance. >> > >> > Can you explain this more? I’m not sure what’s meant by “scope-level >> > inheritance” here. >> > >> >> I don't intend to stop [...] >> > >> > I very much admire your dedication! I’m also digging the discussion. I >> > think >> > we may be representing viewpoints at opposite extremes here, so it’s an >> > interesting contrast, but it also probably means we may be lacking some >> > context for understanding one another’s angles. I’d be curious to hear >> > more >> > about what you see as the problems with the current fields proposal + >> > how >> > your members proposal would solve them; the repo readme didn’t seem to >> > include a rationale section. >> > >> > On Sat, Jul 28, 2018 at 10:30 PM Ranando King <kingmph at gmail.com> wrote: >> >> >> >> I've almost given up on making any significant headway in either >> >> adjusting >> >> or flat-out correcting the flaws in that proposal, but I don't intend >> >> to >> >> stop trying until either we get stuck with that proposal, or they >> >> understand >> >> and accept what I'm telling them, or logically prove that my concerns >> >> are >> >> either irrational or inconsequential. >> >> >> >> > Private object state in particular is only _made complex_ by >> >> > associating >> >> > it with declarations instead of scopes that happen to contain >> >> > declarations >> >> > (or into which constructors are passed, etc). The complexity is >> >> > artificial — >> >> > not a good sign imo. >> >> >> >> That's not quite right. What you're essentially asking for is a >> >> violatable >> >> private field, or as has been described by others, a "soft private". >> >> Since >> >> we agree that the "friendly" & "befriend" pair is a somewhat (if not >> >> completely) bad idea, I'm going to take 1 more pass at your 3 requests >> >> with >> >> a different angle. >> >> >> >> > Adding the same “slot” to multiple classes which don’t inherit from >> >> > each >> >> > other >> >> > Selectively sharing access to private state through functions >> >> > declared >> >> > outside the class body >> >> >> >> ```js >> >> //Using my proposal >> >> var {A, B, C} = (() => { >> >> const common = Symbol("common"); >> >> >> >> class A { >> >> private [common] = 1; >> >> add(...args) { >> >> var retval = this#[common]; >> >> for (let obj of args) { >> >> retval += obj#[common]; >> >> } >> >> return retval; >> >> } >> >> } >> >> class B { >> >> private [common] = 2; >> >> optional() { >> >> console.log(`common member = ${this#[common]}`); >> >> } >> >> } >> >> var C = { >> >> private [common]: 3, >> >> required() { >> >> console.log(`common member = ${this#[common]}`); >> >> } >> >> } >> >> >> >> return { A, B, C }; >> >> })(); >> >> >> >> //So you want the following statement to not throw a TypeError and >> >> return >> >> 6 >> >> (new A()).add(new B(), C); >> >> ``` >> >> I'm not sure I can make this work in my proposal, and I'm absolutely >> >> sure >> >> you'd be flatly refused by the other proposal. If a `Symbol` is >> >> provided as >> >> the `[[IdentifierName]]` of a private or protected field, then I can >> >> let >> >> that `Symbol` be both the key and value that are added to the >> >> `[[DeclarationInfo]]` and `[[InheritanceInfo]]` records. That way there >> >> will >> >> be a common private field name usable by all 3 objects. However, the >> >> guardian logic tries to verify that the function trying to access the >> >> private fields of an instance is a member of the same or descending >> >> prototype that was used to create that instance. If I removed that >> >> requirement, it would work. However, there'd be no way to keep the >> >> private >> >> data from being leaked. Sadly, it's all or nothing with this approach. >> >> Hard >> >> private or soft private, those are the only choices. The TC39 board has >> >> already decided that what they want new syntax for is hard private. >> >> >> >> > Adding slots dynamically, e.g. when adding mix-in methods that may >> >> > initialize a new slot if necessary when called, since subclassing is >> >> > not >> >> > always appropriate >> >> >> >> Because the TC39 board has set their sights on hard private, this will >> >> require new syntax like what I suggested earlier Adding private members >> >> dynamically would also pose a leak risk if it could be done after the >> >> prototype has been fully constructed. The main reason the privacy is >> >> set on >> >> a declaration level is because scope-level inheritance isn't very good >> >> for >> >> `class`-oriented inheritance. The `class` keyword was provided to >> >> simplify >> >> the vertical inheritance model, along with some API to enable >> >> inheritance >> >> from native objects even without using `class`. The syntax changes for >> >> simplifying private field declaration are just an extension of that. >> >> Even >> >> though it's not unusual for some developers to spend a lot of time >> >> working >> >> with fringe use-cases, syntax changes are almost always going to be >> >> made for >> >> the most common use cases first. Maybe after the private fields problem >> >> has >> >> been resolved, someone will figure out a better way to handle your use >> >> cases. >> >> >> >> >> >> On Sat, Jul 28, 2018 at 3:52 PM Darien Valentine >> >> <valentinium at gmail.com> >> >> wrote: >> >>> >> >>> > Are you saying you want multiple non-hierarchally related classes to >> >>> > have an instance private field with shared name [...] >> >>> >> >>> Yeah. This is a hard problem to solve when trying to integrate private >> >>> fields with class syntax, but it’s not a problem at all when privacy >> >>> is a >> >>> more generic tool based on scope. This also isn’t a foreign concept in >> >>> ES: >> >>> consider this intrinsic method: >> >>> >> >>> https://tc39.github.io/ecma262/#sec-arraybuffer.isview >> >>> >> >>> This method returns true if the argument has the >> >>> `[[ViewedArrayBuffer]]` >> >>> slot. This slot exists on genuine instances of both `%TypedArray%` and >> >>> `%DataView%`, but they do not receive these slots by way of >> >>> inheritance from >> >>> a common constructor. There are similar cases in HTML host APIs. >> >>> >> >>> > The befriend keyword would allow an object to request friendship >> >>> > with >> >>> > an existing friendly object. I'm not sure this is a good idea, >> >>> > though. >> >>> >> >>> I don’t think it is either, no. It’s too much complexity for too >> >>> little >> >>> gain. But again, this is achievable “for free” just by divorcing >> >>> “private >> >>> object state” from class declarations (or object literals). I would >> >>> ask: >> >>> what problem is solved by making this a feature of the declarations >> >>> themselves? Does it merit the complexity and the hoop jumping needed >> >>> to >> >>> handle edge cases?\* >> >>> >> >>> \* One person’s edge case; another’s everyday concern haha. >> >>> >> >>> > The example you gave above still declares the functions in question >> >>> > inside the class body, so that's not really a solution. >> >>> >> >>> If you’re referring to the first example, that is a demonstration of >> >>> what >> >>> is possible using the existing stage 3 class fields proposal as >> >>> implemented >> >>> in Chrome. It isn’t what I want; it’s what’s necessary to achieve this >> >>> with >> >>> the current stage 3 proposed model. >> >>> >> >>> > Sounds to me like you'd love for class syntax to look like this >> >>> > [[example with mixin syntax in declaration]] >> >>> >> >>> Perhaps — it’s interesting for sure! But the pattern that already >> >>> works, >> >>> `mixin(Cstr)`, is not presently a source of problems for me. Private >> >>> object >> >>> state in particular is only _made complex_ by associating it with >> >>> declarations instead of scopes that happen to contain declarations (or >> >>> into >> >>> which constructors are passed, etc). The complexity is artificial — >> >>> not a >> >>> good sign imo. >> >>> >> >>> > One thing both proposal-class-fields and proposal-object-members >> >>> > have >> >>> > in common is that the focus is on producing instance-private fields. >> >>> > All 3 >> >>> > of the scenarios you presented lay outside of that focus for one >> >>> > reason or >> >>> > another. >> >>> >> >>> Both the WeakMap solution and the stub concept I provided after are >> >>> more >> >>> generic than privacy in either of those proposals. When I say "object >> >>> private state," it’s true that the object in question could be any >> >>> object. >> >>> But in practice, any realization of the feature would pertain chiefly >> >>> to >> >>> class instances, and the examples I gave, though contrived, do concern >> >>> class >> >>> instances. The reason private object state is chiefly an issue of >> >>> class >> >>> instances stems directly from the nature of prototype methods and >> >>> accessors, >> >>> so if you are not making use of prototypes, you could instead have >> >>> used a >> >>> closure+factory directly. >> >>> >> >>> --- >> >>> >> >>> In a nutshell, my issue with existing proposals could probably be >> >>> summarized as a concern that they are neither as generic nor as simple >> >>> as >> >>> native slots. To be clear, proper “slots” are an internal concept, >> >>> only >> >>> observable indirectly — but they are the special sauce underlying a >> >>> number >> >>> of behaviors which are presently awkward to achieve in ES code itself, >> >>> and >> >>> they are a nice simple model of private object state which is >> >>> tantalizingly >> >>> close to, but not _exactly_ the same as in two critical ways, symbol >> >>> keyed >> >>> properties. That said, “real” slots would continue to have an >> >>> advantage with >> >>> regard to cross-realm stuff even if private symbol keys existed. >> >>> >> >>> That such a model is radically simpler — minmax and all that — feels >> >>> very >> >>> important to me, but I dunno. I’m not holding my breath for big >> >>> changes >> >>> here. The current stage 3 proposal seems to be unstoppable; much >> >>> smarter / >> >>> more important people than me have already tried and failed. :) >> >>> >> >>> >> >>> On Sat, Jul 28, 2018 at 3:14 PM Ranando King <kingmph at gmail.com> >> >>> wrote: >> >>>> >> >>>> In a word... wow. You've got me thinking hard here. Those are some >> >>>> peculiar use cases, and they do a great job of highlighting why >> >>>> someone >> >>>> might forego using `class`. One thing both proposal-class-fields and >> >>>> proposal-object-members have in common is that the focus is on >> >>>> producing >> >>>> instance-private fields. All 3 of the scenarios you presented lay >> >>>> outside of >> >>>> that focus for one reason or another. >> >>>> >> >>>> > Adding the same “slot” to multiple classes which don’t inherit from >> >>>> > each other >> >>>> >> >>>> I'm a little confused by this one. Are you saying you want multiple >> >>>> non-hierarchally related classes to have an instance private field >> >>>> with >> >>>> shared name, such that the same private field name refers to a >> >>>> distinct and >> >>>> separate field on each instance of every such class, but where any >> >>>> such >> >>>> instance can have that field referenced by that shared name from any >> >>>> member >> >>>> function of the corresponding classes? (Wow that was wordy to write >> >>>> out...) >> >>>> If this is what you meant, you're describing friend classes. The >> >>>> top-down >> >>>> processing nature of ES makes this a difficult thing to create a >> >>>> clean >> >>>> syntax for without risking leaking the private state or fundamentally >> >>>> altering how ES is processed. Mutual friendship is even harder. >> >>>> >> >>>> ... and yet I just thought of a way to do it. By telling you this I'm >> >>>> leaving myself to consider writing a proposal containing 2 new >> >>>> keywords: >> >>>> `befriend` and `friendly`. I don't know if this can be done with the >> >>>> existing proposal being what it is. However, with my proposal, >> >>>> there's a >> >>>> chance. The `friendly` keyword would declare that an object is >> >>>> prepared to >> >>>> share select information with any object that befriends it. The >> >>>> `befriend` >> >>>> keyword would allow an object to request friendship with an existing >> >>>> friendly object. I'm not sure this is a good idea, though. This means >> >>>> that >> >>>> any object declared 'friendly' is automatically insecure as all it >> >>>> takes to >> >>>> gain access to the selected members of its private space would be to >> >>>> 'befriend' it. >> >>>> >> >>>> > Selectively sharing access to private state through functions >> >>>> > declared >> >>>> > outside the class body >> >>>> >> >>>> The example you gave above still declares the functions in question >> >>>> inside the `class` body, so that's not really a solution. If the >> >>>> example you >> >>>> gave actually solves your use case, then what you're asking for here >> >>>> isn't >> >>>> even needed. If, however, that was a bad example, then it sounds like >> >>>> you're >> >>>> looking for friend functions. See the previous section. >> >>>> >> >>>> > Adding slots dynamically, e.g. when adding mix-in methods that may >> >>>> > initialize a new slot if necessary when called, since subclassing >> >>>> > is not >> >>>> > always appropriate >> >>>> >> >>>> Sounds to me like you'd love for `class` syntax to look like this: >> >>>> >> >>>> ```js >> >>>> class [<identifierName1>] [extends <identifierName2>] [mixes >> >>>> <identifierName3>[, <identifierName3>[, ...]]] { ... } >> >>>> ``` >> >>>> so that the private fields of the objects in the `mixes` list are >> >>>> added >> >>>> to the set of private fields provided by the `class` definition >> >>>> directly. >> >>>> That would also require another proposal, but I think that can be >> >>>> done >> >>>> regardless of which instance-private fields proposal gets accepted. >> >>>> >> >>>> On Sat, Jul 28, 2018 at 12:49 PM Darien Valentine >> >>>> <valentinium at gmail.com> wrote: >> >>>>> >> >>>>> To put this another, much briefer way, here’s a hypothetical model >> >>>>> for >> >>>>> associating private state with objects that would cover me. Privacy >> >>>>> would be >> >>>>> provided... >> >>>>> >> >>>>> 1. in the form of symbolic keys whose presence cannot be observed >> >>>>> (i.e., they would not be exposed by `getOwnPropertySymbols`) >> >>>>> 2. and which have a syntactic declaration so that one can be sure >> >>>>> they >> >>>>> are really getting private keys (i.e., an api like >> >>>>> `Symbol.private()` >> >>>>> wouldn’t work) >> >>>>> >> >>>>> ``` >> >>>>> const bar = private(); >> >>>>> >> >>>>> // alternatively: const #bar; could be anything so long as it’s >> >>>>> syntactic >> >>>>> >> >>>>> class Foo { >> >>>>> constructor() { >> >>>>> this[bar] = 1; >> >>>>> } >> >>>>> } >> >>>>> >> >>>>> // etc >> >>>>> ``` >> >>>>> >> >>>>> The keys would be typeof 'symbol'; the only difference being that >> >>>>> they >> >>>>> are symbols which are flagged as private when created. They would be >> >>>>> permitted only in syntactic property assignments and accesses. >> >>>>> Existing >> >>>>> reflection utilities would disallow the use or appearance of such >> >>>>> symbols >> >>>>> both to ensure privacy and to maintain the invariant that they are >> >>>>> always >> >>>>> simple data properties: >> >>>>> >> >>>>> ```js >> >>>>> Reflect.defineProperty({}, #bar, { ... }); // throws type error >> >>>>> Object.getOwnPropertyDescriptors(someObjWithAPrivateSlot); // does >> >>>>> not >> >>>>> include it >> >>>>> foo[bar] = 2; // fine >> >>>>> ``` >> >>>>> >> >>>>> This is significantly simpler than what’s in flight both in terms of >> >>>>> syntax and mechanics, which makes me suspicious that I’m probably >> >>>>> ignoring >> >>>>> things that other people find important. However it would bring >> >>>>> parity to ES >> >>>>> objects wrt being able to implement genuinely private slots in >> >>>>> userland with >> >>>>> the same flexibility as what is done internally. >> >>>>> >> >>>>> In total, this entails a new primary expression, a boolean flag >> >>>>> associated with symbol values, and an extra step added to several >> >>>>> algorithms >> >>>>> associated with Object and Reflect. >> >>>>> _______________________________________________ >> >>>>> 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 >> >
Private symbols sounds like an easy win. They would be painfully simple, real properties, not just variables with property imitation syntax that undoubtedly confuses people. With the added benefit that children can truly override the base class, freedom to define private members shared across otherwise unrelated objects, and even injection. My only concern is that it could cross into WeakMap use cases.
Private symbols sounds like an easy win. They would be painfully simple, real properties, not just variables with property imitation syntax that undoubtedly confuses people. With the added benefit that children can truly override the base class, freedom to define private members shared across otherwise unrelated objects, and even injection. My only concern is that it could cross into WeakMap use cases. On Sunday, July 29, 2018, Isiah Meadows <isiahmeadows at gmail.com> wrote: > BTW, I came up with an alternate proposal for privacy altogether: > https://github.com/tc39/proposal-class-fields/issues/115 > > TL;DR: private symbols that proxies can't see and that can't be enumerated. > ----- > > Isiah Meadows > me at isiahmeadows.com > www.isiahmeadows.com > > > On Sun, Jul 29, 2018 at 12:23 AM, Darien Valentine > <valentinium at gmail.com> wrote: > >> What you're essentially asking for is a violatable private field, or as > >> has been described by others, a "soft private". > > > > We might have different definitions here, but I would describe what I’m > > talking about as hard private. Soft private, at least as it appears to > have > > been defined in [prior > > discussions](https://github.com/tc39/proposal-private-fields/issues/33), > > described an avenue where symbol keyed properties were given a new > syntactic > > form — but they were still just regular symbol keys, and therefore could > be > > introspected by outside agents who had not been given express privilege > to > > do so: > > > >> [...] the core would be that "private state" is simply (public) > >> symbol-named properties, with syntactic sugar for those symbols, and > >> possibly some kind of introspection over them [...] > > > > The thread goes on to contrast the soft model with an earlier version of > the > > private fields proposal seen today. The hard private example uses the > class > > declaration as a pseudo-scope, but contrasting these two options as if > they > > are binary is not accurate: hard private through module/function/block > scope > > already exists, it is just difficult to work with in the context of > shared > > prototypes — one must either use WeakMaps, technically giving _hardness_ > > because of the forgeability of `global.WeakMap` / `WeakMap.prototype` / > > `WeakMap.prototype.get|has|set`, or be willing to either not worry about > > garbage collection or implement it manually. This could be solved for > with a > > few rather undramatic changes, though. > > > > Notably, the first post there lists the following as a disadvantage of > the > > soft model it describes: > > > >> Platform objects, both within ECMAScript and in embedding environments, > >> contain hard private state. If a library wants to be high-fidelity and > just > >> like a platform object, soft-private state does not provide this > (@domenic) > > > > ...but neither model there quite covers that use case. Platform objects > > _can_ see each other’s private state (cf the `isView` example earlier, or > > scan the DOM API specs / Chrome source a bit to find numerous examples). > > It’s only the ES layer interacting with their interfaces that cannot. > > > > Such things can be achieved with ordinary scope, which is why the WeakMap > > pattern has worked in practice in my experience to date, while > > class-declaration-scoped privacy has not. It isn’t uncommon for a > library’s > > exposed interface to be composed of an object graph, where privacy is a > > concern at this public interface level, but library internal state may be > > interconnected in unexposed ways under the hood. The most familiar > example > > of this is a DOM node tree. As an experiment, perhaps try to implement > the > > relationships between HTMLFormElement, HTMLFormControlsCollection and the > > various form control elements using either the main private fields > proposal > > or your alternative proposal and see what happens. > > > >> However, the guardian logic tries to verify that the function trying to > >> access the private fields of an instance is a member of the same or > >> descending prototype that was used to create that instance. > > > > Because I’m looking at this in terms of slots, I’d first point out that > > prototypes don’t determine slottedness, the execution of some specific > > constructor does. It’s during this process that slots are associated with > > the newly minted object by its identity. But even the current private > fields > > proposal tracks this behavior closely, and I’m not sure how else it could > > work. The [[Prototype]] slot of an object is typically mutable > > (`R|O.setPrototypeOf`, `__proto__`) and forgeable (Proxy’s > `getPrototypeOf` > > trap). Why/how would its value matter when it comes to accessing private > > state? > > > > ```js > > const pattern = /foo/; > > Reflect.setPrototypeOf(pattern, Date.prototype); > > pattern instanceof Date; // true > > pattern instanceof RegExp; // false > > pattern.getMinutes(); // throws TypeError because [[DateValue]] slot is > > missing > > RegExp.prototype.exec.call(pattern, 'foo'); // works; object has RegExp > > private slots > > ``` > > > >> If I removed that requirement, it would work. However, there'd be no way > >> to keep the private data from being leaked. Sadly, it's all or nothing > with > >> this approach. Hard private or soft private, those are the only choices. > > > > In the context of what you’ve described here this may be true, but no > such > > limitation presently exists. We can already do all this — hard, leak-free > > privacy, brandedness, “friends” etc — with scopes and WeakMaps, but for > the > > fact that the `WeakMap` intrinsics may be forged. So what’s baffled me is > > this: why are all the proposals exploring this space not addressing that > > relatively simple existing problem, and instead starting off from a > place of > > significant new complexity? You said “maybe after the private fields > problem > > has been resolved, someone will figure out a better way to handle your > use > > cases,” but I’d have hoped for the opposite — I want the primitive > building > > blocks which things like class field syntax could be built over, if it is > > found that they are still necessary once the root issue is solved for. > > > >> The main reason the privacy is set on a declaration level is because > >> scope-level inheritance isn't very good for class-oriented inheritance. > > > > Can you explain this more? I’m not sure what’s meant by “scope-level > > inheritance” here. > > > >> I don't intend to stop [...] > > > > I very much admire your dedication! I’m also digging the discussion. I > think > > we may be representing viewpoints at opposite extremes here, so it’s an > > interesting contrast, but it also probably means we may be lacking some > > context for understanding one another’s angles. I’d be curious to hear > more > > about what you see as the problems with the current fields proposal + how > > your members proposal would solve them; the repo readme didn’t seem to > > include a rationale section. > > > > On Sat, Jul 28, 2018 at 10:30 PM Ranando King <kingmph at gmail.com> wrote: > >> > >> I've almost given up on making any significant headway in either > adjusting > >> or flat-out correcting the flaws in that proposal, but I don't intend to > >> stop trying until either we get stuck with that proposal, or they > understand > >> and accept what I'm telling them, or logically prove that my concerns > are > >> either irrational or inconsequential. > >> > >> > Private object state in particular is only _made complex_ by > associating > >> > it with declarations instead of scopes that happen to contain > declarations > >> > (or into which constructors are passed, etc). The complexity is > artificial — > >> > not a good sign imo. > >> > >> That's not quite right. What you're essentially asking for is a > violatable > >> private field, or as has been described by others, a "soft private". > Since > >> we agree that the "friendly" & "befriend" pair is a somewhat (if not > >> completely) bad idea, I'm going to take 1 more pass at your 3 requests > with > >> a different angle. > >> > >> > Adding the same “slot” to multiple classes which don’t inherit from > each > >> > other > >> > Selectively sharing access to private state through functions declared > >> > outside the class body > >> > >> ```js > >> //Using my proposal > >> var {A, B, C} = (() => { > >> const common = Symbol("common"); > >> > >> class A { > >> private [common] = 1; > >> add(...args) { > >> var retval = this#[common]; > >> for (let obj of args) { > >> retval += obj#[common]; > >> } > >> return retval; > >> } > >> } > >> class B { > >> private [common] = 2; > >> optional() { > >> console.log(`common member = ${this#[common]}`); > >> } > >> } > >> var C = { > >> private [common]: 3, > >> required() { > >> console.log(`common member = ${this#[common]}`); > >> } > >> } > >> > >> return { A, B, C }; > >> })(); > >> > >> //So you want the following statement to not throw a TypeError and > return > >> 6 > >> (new A()).add(new B(), C); > >> ``` > >> I'm not sure I can make this work in my proposal, and I'm absolutely > sure > >> you'd be flatly refused by the other proposal. If a `Symbol` is > provided as > >> the `[[IdentifierName]]` of a private or protected field, then I can let > >> that `Symbol` be both the key and value that are added to the > >> `[[DeclarationInfo]]` and `[[InheritanceInfo]]` records. That way there > will > >> be a common private field name usable by all 3 objects. However, the > >> guardian logic tries to verify that the function trying to access the > >> private fields of an instance is a member of the same or descending > >> prototype that was used to create that instance. If I removed that > >> requirement, it would work. However, there'd be no way to keep the > private > >> data from being leaked. Sadly, it's all or nothing with this approach. > Hard > >> private or soft private, those are the only choices. The TC39 board has > >> already decided that what they want new syntax for is hard private. > >> > >> > Adding slots dynamically, e.g. when adding mix-in methods that may > >> > initialize a new slot if necessary when called, since subclassing is > not > >> > always appropriate > >> > >> Because the TC39 board has set their sights on hard private, this will > >> require new syntax like what I suggested earlier Adding private members > >> dynamically would also pose a leak risk if it could be done after the > >> prototype has been fully constructed. The main reason the privacy is > set on > >> a declaration level is because scope-level inheritance isn't very good > for > >> `class`-oriented inheritance. The `class` keyword was provided to > simplify > >> the vertical inheritance model, along with some API to enable > inheritance > >> from native objects even without using `class`. The syntax changes for > >> simplifying private field declaration are just an extension of that. > Even > >> though it's not unusual for some developers to spend a lot of time > working > >> with fringe use-cases, syntax changes are almost always going to be > made for > >> the most common use cases first. Maybe after the private fields problem > has > >> been resolved, someone will figure out a better way to handle your use > >> cases. > >> > >> > >> On Sat, Jul 28, 2018 at 3:52 PM Darien Valentine <valentinium at gmail.com > > > >> wrote: > >>> > >>> > Are you saying you want multiple non-hierarchally related classes to > >>> > have an instance private field with shared name [...] > >>> > >>> Yeah. This is a hard problem to solve when trying to integrate private > >>> fields with class syntax, but it’s not a problem at all when privacy > is a > >>> more generic tool based on scope. This also isn’t a foreign concept in > ES: > >>> consider this intrinsic method: > >>> > >>> https://tc39.github.io/ecma262/#sec-arraybuffer.isview > >>> > >>> This method returns true if the argument has the > `[[ViewedArrayBuffer]]` > >>> slot. This slot exists on genuine instances of both `%TypedArray%` and > >>> `%DataView%`, but they do not receive these slots by way of > inheritance from > >>> a common constructor. There are similar cases in HTML host APIs. > >>> > >>> > The befriend keyword would allow an object to request friendship with > >>> > an existing friendly object. I'm not sure this is a good idea, > though. > >>> > >>> I don’t think it is either, no. It’s too much complexity for too little > >>> gain. But again, this is achievable “for free” just by divorcing > “private > >>> object state” from class declarations (or object literals). I would > ask: > >>> what problem is solved by making this a feature of the declarations > >>> themselves? Does it merit the complexity and the hoop jumping needed to > >>> handle edge cases?\* > >>> > >>> \* One person’s edge case; another’s everyday concern haha. > >>> > >>> > The example you gave above still declares the functions in question > >>> > inside the class body, so that's not really a solution. > >>> > >>> If you’re referring to the first example, that is a demonstration of > what > >>> is possible using the existing stage 3 class fields proposal as > implemented > >>> in Chrome. It isn’t what I want; it’s what’s necessary to achieve this > with > >>> the current stage 3 proposed model. > >>> > >>> > Sounds to me like you'd love for class syntax to look like this > >>> > [[example with mixin syntax in declaration]] > >>> > >>> Perhaps — it’s interesting for sure! But the pattern that already > works, > >>> `mixin(Cstr)`, is not presently a source of problems for me. Private > object > >>> state in particular is only _made complex_ by associating it with > >>> declarations instead of scopes that happen to contain declarations (or > into > >>> which constructors are passed, etc). The complexity is artificial — > not a > >>> good sign imo. > >>> > >>> > One thing both proposal-class-fields and proposal-object-members > have > >>> > in common is that the focus is on producing instance-private fields. > All 3 > >>> > of the scenarios you presented lay outside of that focus for one > reason or > >>> > another. > >>> > >>> Both the WeakMap solution and the stub concept I provided after are > more > >>> generic than privacy in either of those proposals. When I say "object > >>> private state," it’s true that the object in question could be any > object. > >>> But in practice, any realization of the feature would pertain chiefly > to > >>> class instances, and the examples I gave, though contrived, do concern > class > >>> instances. The reason private object state is chiefly an issue of class > >>> instances stems directly from the nature of prototype methods and > accessors, > >>> so if you are not making use of prototypes, you could instead have > used a > >>> closure+factory directly. > >>> > >>> --- > >>> > >>> In a nutshell, my issue with existing proposals could probably be > >>> summarized as a concern that they are neither as generic nor as simple > as > >>> native slots. To be clear, proper “slots” are an internal concept, only > >>> observable indirectly — but they are the special sauce underlying a > number > >>> of behaviors which are presently awkward to achieve in ES code itself, > and > >>> they are a nice simple model of private object state which is > tantalizingly > >>> close to, but not _exactly_ the same as in two critical ways, symbol > keyed > >>> properties. That said, “real” slots would continue to have an > advantage with > >>> regard to cross-realm stuff even if private symbol keys existed. > >>> > >>> That such a model is radically simpler — minmax and all that — feels > very > >>> important to me, but I dunno. I’m not holding my breath for big changes > >>> here. The current stage 3 proposal seems to be unstoppable; much > smarter / > >>> more important people than me have already tried and failed. :) > >>> > >>> > >>> On Sat, Jul 28, 2018 at 3:14 PM Ranando King <kingmph at gmail.com> > wrote: > >>>> > >>>> In a word... wow. You've got me thinking hard here. Those are some > >>>> peculiar use cases, and they do a great job of highlighting why > someone > >>>> might forego using `class`. One thing both proposal-class-fields and > >>>> proposal-object-members have in common is that the focus is on > producing > >>>> instance-private fields. All 3 of the scenarios you presented lay > outside of > >>>> that focus for one reason or another. > >>>> > >>>> > Adding the same “slot” to multiple classes which don’t inherit from > >>>> > each other > >>>> > >>>> I'm a little confused by this one. Are you saying you want multiple > >>>> non-hierarchally related classes to have an instance private field > with > >>>> shared name, such that the same private field name refers to a > distinct and > >>>> separate field on each instance of every such class, but where any > such > >>>> instance can have that field referenced by that shared name from any > member > >>>> function of the corresponding classes? (Wow that was wordy to write > out...) > >>>> If this is what you meant, you're describing friend classes. The > top-down > >>>> processing nature of ES makes this a difficult thing to create a clean > >>>> syntax for without risking leaking the private state or fundamentally > >>>> altering how ES is processed. Mutual friendship is even harder. > >>>> > >>>> ... and yet I just thought of a way to do it. By telling you this I'm > >>>> leaving myself to consider writing a proposal containing 2 new > keywords: > >>>> `befriend` and `friendly`. I don't know if this can be done with the > >>>> existing proposal being what it is. However, with my proposal, > there's a > >>>> chance. The `friendly` keyword would declare that an object is > prepared to > >>>> share select information with any object that befriends it. The > `befriend` > >>>> keyword would allow an object to request friendship with an existing > >>>> friendly object. I'm not sure this is a good idea, though. This means > that > >>>> any object declared 'friendly' is automatically insecure as all it > takes to > >>>> gain access to the selected members of its private space would be to > >>>> 'befriend' it. > >>>> > >>>> > Selectively sharing access to private state through functions > declared > >>>> > outside the class body > >>>> > >>>> The example you gave above still declares the functions in question > >>>> inside the `class` body, so that's not really a solution. If the > example you > >>>> gave actually solves your use case, then what you're asking for here > isn't > >>>> even needed. If, however, that was a bad example, then it sounds like > you're > >>>> looking for friend functions. See the previous section. > >>>> > >>>> > Adding slots dynamically, e.g. when adding mix-in methods that may > >>>> > initialize a new slot if necessary when called, since subclassing > is not > >>>> > always appropriate > >>>> > >>>> Sounds to me like you'd love for `class` syntax to look like this: > >>>> > >>>> ```js > >>>> class [<identifierName1>] [extends <identifierName2>] [mixes > >>>> <identifierName3>[, <identifierName3>[, ...]]] { ... } > >>>> ``` > >>>> so that the private fields of the objects in the `mixes` list are > added > >>>> to the set of private fields provided by the `class` definition > directly. > >>>> That would also require another proposal, but I think that can be done > >>>> regardless of which instance-private fields proposal gets accepted. > >>>> > >>>> On Sat, Jul 28, 2018 at 12:49 PM Darien Valentine > >>>> <valentinium at gmail.com> wrote: > >>>>> > >>>>> To put this another, much briefer way, here’s a hypothetical model > for > >>>>> associating private state with objects that would cover me. Privacy > would be > >>>>> provided... > >>>>> > >>>>> 1. in the form of symbolic keys whose presence cannot be observed > >>>>> (i.e., they would not be exposed by `getOwnPropertySymbols`) > >>>>> 2. and which have a syntactic declaration so that one can be sure > they > >>>>> are really getting private keys (i.e., an api like `Symbol.private()` > >>>>> wouldn’t work) > >>>>> > >>>>> ``` > >>>>> const bar = private(); > >>>>> > >>>>> // alternatively: const #bar; could be anything so long as it’s > >>>>> syntactic > >>>>> > >>>>> class Foo { > >>>>> constructor() { > >>>>> this[bar] = 1; > >>>>> } > >>>>> } > >>>>> > >>>>> // etc > >>>>> ``` > >>>>> > >>>>> The keys would be typeof 'symbol'; the only difference being that > they > >>>>> are symbols which are flagged as private when created. They would be > >>>>> permitted only in syntactic property assignments and accesses. > Existing > >>>>> reflection utilities would disallow the use or appearance of such > symbols > >>>>> both to ensure privacy and to maintain the invariant that they are > always > >>>>> simple data properties: > >>>>> > >>>>> ```js > >>>>> Reflect.defineProperty({}, #bar, { ... }); // throws type error > >>>>> Object.getOwnPropertyDescriptors(someObjWithAPrivateSlot); // does > not > >>>>> include it > >>>>> foo[bar] = 2; // fine > >>>>> ``` > >>>>> > >>>>> This is significantly simpler than what’s in flight both in terms of > >>>>> syntax and mechanics, which makes me suspicious that I’m probably > ignoring > >>>>> things that other people find important. However it would bring > parity to ES > >>>>> objects wrt being able to implement genuinely private slots in > userland with > >>>>> the same flexibility as what is done internally. > >>>>> > >>>>> In total, this entails a new primary expression, a boolean flag > >>>>> associated with symbol values, and an extra step added to several > algorithms > >>>>> associated with Object and Reflect. > >>>>> _______________________________________________ > >>>>> 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/20180729/eeca2c02/attachment-0001.html>
It will, but weak maps will still remain useful for cases when you're semantically dealing with a key/value map. In theory, you could implement a weak map on top of this 1, but in practice, it doesn't always make sense to do it. A good example of this is if you are "tagging" an object with data. If this data isn't really part of the object itself, you shouldn't be using a private symbol for it. Another good example is if you're doing simple caching and you need to clear the weak map by replacing it. Using private symbols for this doesn't really fit with the domain here, so you're more likely just to confuse future readers (including yourself) if you do this.
Isiah Meadows me at isiahmeadows.com, www.isiahmeadows.com
It will, but weak maps will still remain useful for cases when you're semantically dealing with a key/value map. In theory, you could implement a weak map on top of this [1], but in practice, it doesn't always make sense to do it. A good example of this is if you are "tagging" an object with data. If this data isn't really part of the object itself, you shouldn't be using a private symbol for it. Another good example is if you're doing simple caching and you need to clear the weak map by replacing it. Using private symbols for this doesn't really fit with the domain here, so you're more likely just to confuse future readers (including yourself) if you do this. [1]: https://gist.github.com/isiahmeadows/a8494868c4b193dfbf7139589f472ad8 ----- Isiah Meadows me at isiahmeadows.com www.isiahmeadows.com On Sun, Jul 29, 2018 at 10:05 PM, Michael Theriot <michael.lee.theriot at gmail.com> wrote: > Private symbols sounds like an easy win. They would be painfully simple, > real properties, not just variables with property imitation syntax that > undoubtedly confuses people. With the added benefit that children can truly > override the base class, freedom to define private members shared across > otherwise unrelated objects, and even injection. My only concern is that it > could cross into WeakMap use cases. > > > On Sunday, July 29, 2018, Isiah Meadows <isiahmeadows at gmail.com> wrote: >> >> BTW, I came up with an alternate proposal for privacy altogether: >> https://github.com/tc39/proposal-class-fields/issues/115 >> >> TL;DR: private symbols that proxies can't see and that can't be >> enumerated. >> ----- >> >> Isiah Meadows >> me at isiahmeadows.com >> www.isiahmeadows.com >> >> >> On Sun, Jul 29, 2018 at 12:23 AM, Darien Valentine >> <valentinium at gmail.com> wrote: >> >> What you're essentially asking for is a violatable private field, or as >> >> has been described by others, a "soft private". >> > >> > We might have different definitions here, but I would describe what I’m >> > talking about as hard private. Soft private, at least as it appears to >> > have >> > been defined in [prior >> > discussions](https://github.com/tc39/proposal-private-fields/issues/33), >> > described an avenue where symbol keyed properties were given a new >> > syntactic >> > form — but they were still just regular symbol keys, and therefore could >> > be >> > introspected by outside agents who had not been given express privilege >> > to >> > do so: >> > >> >> [...] the core would be that "private state" is simply (public) >> >> symbol-named properties, with syntactic sugar for those symbols, and >> >> possibly some kind of introspection over them [...] >> > >> > The thread goes on to contrast the soft model with an earlier version of >> > the >> > private fields proposal seen today. The hard private example uses the >> > class >> > declaration as a pseudo-scope, but contrasting these two options as if >> > they >> > are binary is not accurate: hard private through module/function/block >> > scope >> > already exists, it is just difficult to work with in the context of >> > shared >> > prototypes — one must either use WeakMaps, technically giving _hardness_ >> > because of the forgeability of `global.WeakMap` / `WeakMap.prototype` / >> > `WeakMap.prototype.get|has|set`, or be willing to either not worry about >> > garbage collection or implement it manually. This could be solved for >> > with a >> > few rather undramatic changes, though. >> > >> > Notably, the first post there lists the following as a disadvantage of >> > the >> > soft model it describes: >> > >> >> Platform objects, both within ECMAScript and in embedding environments, >> >> contain hard private state. If a library wants to be high-fidelity and >> >> just >> >> like a platform object, soft-private state does not provide this >> >> (@domenic) >> > >> > ...but neither model there quite covers that use case. Platform objects >> > _can_ see each other’s private state (cf the `isView` example earlier, >> > or >> > scan the DOM API specs / Chrome source a bit to find numerous examples). >> > It’s only the ES layer interacting with their interfaces that cannot. >> > >> > Such things can be achieved with ordinary scope, which is why the >> > WeakMap >> > pattern has worked in practice in my experience to date, while >> > class-declaration-scoped privacy has not. It isn’t uncommon for a >> > library’s >> > exposed interface to be composed of an object graph, where privacy is a >> > concern at this public interface level, but library internal state may >> > be >> > interconnected in unexposed ways under the hood. The most familiar >> > example >> > of this is a DOM node tree. As an experiment, perhaps try to implement >> > the >> > relationships between HTMLFormElement, HTMLFormControlsCollection and >> > the >> > various form control elements using either the main private fields >> > proposal >> > or your alternative proposal and see what happens. >> > >> >> However, the guardian logic tries to verify that the function trying to >> >> access the private fields of an instance is a member of the same or >> >> descending prototype that was used to create that instance. >> > >> > Because I’m looking at this in terms of slots, I’d first point out that >> > prototypes don’t determine slottedness, the execution of some specific >> > constructor does. It’s during this process that slots are associated >> > with >> > the newly minted object by its identity. But even the current private >> > fields >> > proposal tracks this behavior closely, and I’m not sure how else it >> > could >> > work. The [[Prototype]] slot of an object is typically mutable >> > (`R|O.setPrototypeOf`, `__proto__`) and forgeable (Proxy’s >> > `getPrototypeOf` >> > trap). Why/how would its value matter when it comes to accessing private >> > state? >> > >> > ```js >> > const pattern = /foo/; >> > Reflect.setPrototypeOf(pattern, Date.prototype); >> > pattern instanceof Date; // true >> > pattern instanceof RegExp; // false >> > pattern.getMinutes(); // throws TypeError because [[DateValue]] slot is >> > missing >> > RegExp.prototype.exec.call(pattern, 'foo'); // works; object has RegExp >> > private slots >> > ``` >> > >> >> If I removed that requirement, it would work. However, there'd be no >> >> way >> >> to keep the private data from being leaked. Sadly, it's all or nothing >> >> with >> >> this approach. Hard private or soft private, those are the only >> >> choices. >> > >> > In the context of what you’ve described here this may be true, but no >> > such >> > limitation presently exists. We can already do all this — hard, >> > leak-free >> > privacy, brandedness, “friends” etc — with scopes and WeakMaps, but for >> > the >> > fact that the `WeakMap` intrinsics may be forged. So what’s baffled me >> > is >> > this: why are all the proposals exploring this space not addressing that >> > relatively simple existing problem, and instead starting off from a >> > place of >> > significant new complexity? You said “maybe after the private fields >> > problem >> > has been resolved, someone will figure out a better way to handle your >> > use >> > cases,” but I’d have hoped for the opposite — I want the primitive >> > building >> > blocks which things like class field syntax could be built over, if it >> > is >> > found that they are still necessary once the root issue is solved for. >> > >> >> The main reason the privacy is set on a declaration level is because >> >> scope-level inheritance isn't very good for class-oriented inheritance. >> > >> > Can you explain this more? I’m not sure what’s meant by “scope-level >> > inheritance” here. >> > >> >> I don't intend to stop [...] >> > >> > I very much admire your dedication! I’m also digging the discussion. I >> > think >> > we may be representing viewpoints at opposite extremes here, so it’s an >> > interesting contrast, but it also probably means we may be lacking some >> > context for understanding one another’s angles. I’d be curious to hear >> > more >> > about what you see as the problems with the current fields proposal + >> > how >> > your members proposal would solve them; the repo readme didn’t seem to >> > include a rationale section. >> > >> > On Sat, Jul 28, 2018 at 10:30 PM Ranando King <kingmph at gmail.com> wrote: >> >> >> >> I've almost given up on making any significant headway in either >> >> adjusting >> >> or flat-out correcting the flaws in that proposal, but I don't intend >> >> to >> >> stop trying until either we get stuck with that proposal, or they >> >> understand >> >> and accept what I'm telling them, or logically prove that my concerns >> >> are >> >> either irrational or inconsequential. >> >> >> >> > Private object state in particular is only _made complex_ by >> >> > associating >> >> > it with declarations instead of scopes that happen to contain >> >> > declarations >> >> > (or into which constructors are passed, etc). The complexity is >> >> > artificial — >> >> > not a good sign imo. >> >> >> >> That's not quite right. What you're essentially asking for is a >> >> violatable >> >> private field, or as has been described by others, a "soft private". >> >> Since >> >> we agree that the "friendly" & "befriend" pair is a somewhat (if not >> >> completely) bad idea, I'm going to take 1 more pass at your 3 requests >> >> with >> >> a different angle. >> >> >> >> > Adding the same “slot” to multiple classes which don’t inherit from >> >> > each >> >> > other >> >> > Selectively sharing access to private state through functions >> >> > declared >> >> > outside the class body >> >> >> >> ```js >> >> //Using my proposal >> >> var {A, B, C} = (() => { >> >> const common = Symbol("common"); >> >> >> >> class A { >> >> private [common] = 1; >> >> add(...args) { >> >> var retval = this#[common]; >> >> for (let obj of args) { >> >> retval += obj#[common]; >> >> } >> >> return retval; >> >> } >> >> } >> >> class B { >> >> private [common] = 2; >> >> optional() { >> >> console.log(`common member = ${this#[common]}`); >> >> } >> >> } >> >> var C = { >> >> private [common]: 3, >> >> required() { >> >> console.log(`common member = ${this#[common]}`); >> >> } >> >> } >> >> >> >> return { A, B, C }; >> >> })(); >> >> >> >> //So you want the following statement to not throw a TypeError and >> >> return >> >> 6 >> >> (new A()).add(new B(), C); >> >> ``` >> >> I'm not sure I can make this work in my proposal, and I'm absolutely >> >> sure >> >> you'd be flatly refused by the other proposal. If a `Symbol` is >> >> provided as >> >> the `[[IdentifierName]]` of a private or protected field, then I can >> >> let >> >> that `Symbol` be both the key and value that are added to the >> >> `[[DeclarationInfo]]` and `[[InheritanceInfo]]` records. That way there >> >> will >> >> be a common private field name usable by all 3 objects. However, the >> >> guardian logic tries to verify that the function trying to access the >> >> private fields of an instance is a member of the same or descending >> >> prototype that was used to create that instance. If I removed that >> >> requirement, it would work. However, there'd be no way to keep the >> >> private >> >> data from being leaked. Sadly, it's all or nothing with this approach. >> >> Hard >> >> private or soft private, those are the only choices. The TC39 board has >> >> already decided that what they want new syntax for is hard private. >> >> >> >> > Adding slots dynamically, e.g. when adding mix-in methods that may >> >> > initialize a new slot if necessary when called, since subclassing is >> >> > not >> >> > always appropriate >> >> >> >> Because the TC39 board has set their sights on hard private, this will >> >> require new syntax like what I suggested earlier Adding private members >> >> dynamically would also pose a leak risk if it could be done after the >> >> prototype has been fully constructed. The main reason the privacy is >> >> set on >> >> a declaration level is because scope-level inheritance isn't very good >> >> for >> >> `class`-oriented inheritance. The `class` keyword was provided to >> >> simplify >> >> the vertical inheritance model, along with some API to enable >> >> inheritance >> >> from native objects even without using `class`. The syntax changes for >> >> simplifying private field declaration are just an extension of that. >> >> Even >> >> though it's not unusual for some developers to spend a lot of time >> >> working >> >> with fringe use-cases, syntax changes are almost always going to be >> >> made for >> >> the most common use cases first. Maybe after the private fields problem >> >> has >> >> been resolved, someone will figure out a better way to handle your use >> >> cases. >> >> >> >> >> >> On Sat, Jul 28, 2018 at 3:52 PM Darien Valentine >> >> <valentinium at gmail.com> >> >> wrote: >> >>> >> >>> > Are you saying you want multiple non-hierarchally related classes to >> >>> > have an instance private field with shared name [...] >> >>> >> >>> Yeah. This is a hard problem to solve when trying to integrate private >> >>> fields with class syntax, but it’s not a problem at all when privacy >> >>> is a >> >>> more generic tool based on scope. This also isn’t a foreign concept in >> >>> ES: >> >>> consider this intrinsic method: >> >>> >> >>> https://tc39.github.io/ecma262/#sec-arraybuffer.isview >> >>> >> >>> This method returns true if the argument has the >> >>> `[[ViewedArrayBuffer]]` >> >>> slot. This slot exists on genuine instances of both `%TypedArray%` and >> >>> `%DataView%`, but they do not receive these slots by way of >> >>> inheritance from >> >>> a common constructor. There are similar cases in HTML host APIs. >> >>> >> >>> > The befriend keyword would allow an object to request friendship >> >>> > with >> >>> > an existing friendly object. I'm not sure this is a good idea, >> >>> > though. >> >>> >> >>> I don’t think it is either, no. It’s too much complexity for too >> >>> little >> >>> gain. But again, this is achievable “for free” just by divorcing >> >>> “private >> >>> object state” from class declarations (or object literals). I would >> >>> ask: >> >>> what problem is solved by making this a feature of the declarations >> >>> themselves? Does it merit the complexity and the hoop jumping needed >> >>> to >> >>> handle edge cases?\* >> >>> >> >>> \* One person’s edge case; another’s everyday concern haha. >> >>> >> >>> > The example you gave above still declares the functions in question >> >>> > inside the class body, so that's not really a solution. >> >>> >> >>> If you’re referring to the first example, that is a demonstration of >> >>> what >> >>> is possible using the existing stage 3 class fields proposal as >> >>> implemented >> >>> in Chrome. It isn’t what I want; it’s what’s necessary to achieve this >> >>> with >> >>> the current stage 3 proposed model. >> >>> >> >>> > Sounds to me like you'd love for class syntax to look like this >> >>> > [[example with mixin syntax in declaration]] >> >>> >> >>> Perhaps — it’s interesting for sure! But the pattern that already >> >>> works, >> >>> `mixin(Cstr)`, is not presently a source of problems for me. Private >> >>> object >> >>> state in particular is only _made complex_ by associating it with >> >>> declarations instead of scopes that happen to contain declarations (or >> >>> into >> >>> which constructors are passed, etc). The complexity is artificial — >> >>> not a >> >>> good sign imo. >> >>> >> >>> > One thing both proposal-class-fields and proposal-object-members >> >>> > have >> >>> > in common is that the focus is on producing instance-private fields. >> >>> > All 3 >> >>> > of the scenarios you presented lay outside of that focus for one >> >>> > reason or >> >>> > another. >> >>> >> >>> Both the WeakMap solution and the stub concept I provided after are >> >>> more >> >>> generic than privacy in either of those proposals. When I say "object >> >>> private state," it’s true that the object in question could be any >> >>> object. >> >>> But in practice, any realization of the feature would pertain chiefly >> >>> to >> >>> class instances, and the examples I gave, though contrived, do concern >> >>> class >> >>> instances. The reason private object state is chiefly an issue of >> >>> class >> >>> instances stems directly from the nature of prototype methods and >> >>> accessors, >> >>> so if you are not making use of prototypes, you could instead have >> >>> used a >> >>> closure+factory directly. >> >>> >> >>> --- >> >>> >> >>> In a nutshell, my issue with existing proposals could probably be >> >>> summarized as a concern that they are neither as generic nor as simple >> >>> as >> >>> native slots. To be clear, proper “slots” are an internal concept, >> >>> only >> >>> observable indirectly — but they are the special sauce underlying a >> >>> number >> >>> of behaviors which are presently awkward to achieve in ES code itself, >> >>> and >> >>> they are a nice simple model of private object state which is >> >>> tantalizingly >> >>> close to, but not _exactly_ the same as in two critical ways, symbol >> >>> keyed >> >>> properties. That said, “real” slots would continue to have an >> >>> advantage with >> >>> regard to cross-realm stuff even if private symbol keys existed. >> >>> >> >>> That such a model is radically simpler — minmax and all that — feels >> >>> very >> >>> important to me, but I dunno. I’m not holding my breath for big >> >>> changes >> >>> here. The current stage 3 proposal seems to be unstoppable; much >> >>> smarter / >> >>> more important people than me have already tried and failed. :) >> >>> >> >>> >> >>> On Sat, Jul 28, 2018 at 3:14 PM Ranando King <kingmph at gmail.com> >> >>> wrote: >> >>>> >> >>>> In a word... wow. You've got me thinking hard here. Those are some >> >>>> peculiar use cases, and they do a great job of highlighting why >> >>>> someone >> >>>> might forego using `class`. One thing both proposal-class-fields and >> >>>> proposal-object-members have in common is that the focus is on >> >>>> producing >> >>>> instance-private fields. All 3 of the scenarios you presented lay >> >>>> outside of >> >>>> that focus for one reason or another. >> >>>> >> >>>> > Adding the same “slot” to multiple classes which don’t inherit from >> >>>> > each other >> >>>> >> >>>> I'm a little confused by this one. Are you saying you want multiple >> >>>> non-hierarchally related classes to have an instance private field >> >>>> with >> >>>> shared name, such that the same private field name refers to a >> >>>> distinct and >> >>>> separate field on each instance of every such class, but where any >> >>>> such >> >>>> instance can have that field referenced by that shared name from any >> >>>> member >> >>>> function of the corresponding classes? (Wow that was wordy to write >> >>>> out...) >> >>>> If this is what you meant, you're describing friend classes. The >> >>>> top-down >> >>>> processing nature of ES makes this a difficult thing to create a >> >>>> clean >> >>>> syntax for without risking leaking the private state or fundamentally >> >>>> altering how ES is processed. Mutual friendship is even harder. >> >>>> >> >>>> ... and yet I just thought of a way to do it. By telling you this I'm >> >>>> leaving myself to consider writing a proposal containing 2 new >> >>>> keywords: >> >>>> `befriend` and `friendly`. I don't know if this can be done with the >> >>>> existing proposal being what it is. However, with my proposal, >> >>>> there's a >> >>>> chance. The `friendly` keyword would declare that an object is >> >>>> prepared to >> >>>> share select information with any object that befriends it. The >> >>>> `befriend` >> >>>> keyword would allow an object to request friendship with an existing >> >>>> friendly object. I'm not sure this is a good idea, though. This means >> >>>> that >> >>>> any object declared 'friendly' is automatically insecure as all it >> >>>> takes to >> >>>> gain access to the selected members of its private space would be to >> >>>> 'befriend' it. >> >>>> >> >>>> > Selectively sharing access to private state through functions >> >>>> > declared >> >>>> > outside the class body >> >>>> >> >>>> The example you gave above still declares the functions in question >> >>>> inside the `class` body, so that's not really a solution. If the >> >>>> example you >> >>>> gave actually solves your use case, then what you're asking for here >> >>>> isn't >> >>>> even needed. If, however, that was a bad example, then it sounds like >> >>>> you're >> >>>> looking for friend functions. See the previous section. >> >>>> >> >>>> > Adding slots dynamically, e.g. when adding mix-in methods that may >> >>>> > initialize a new slot if necessary when called, since subclassing >> >>>> > is not >> >>>> > always appropriate >> >>>> >> >>>> Sounds to me like you'd love for `class` syntax to look like this: >> >>>> >> >>>> ```js >> >>>> class [<identifierName1>] [extends <identifierName2>] [mixes >> >>>> <identifierName3>[, <identifierName3>[, ...]]] { ... } >> >>>> ``` >> >>>> so that the private fields of the objects in the `mixes` list are >> >>>> added >> >>>> to the set of private fields provided by the `class` definition >> >>>> directly. >> >>>> That would also require another proposal, but I think that can be >> >>>> done >> >>>> regardless of which instance-private fields proposal gets accepted. >> >>>> >> >>>> On Sat, Jul 28, 2018 at 12:49 PM Darien Valentine >> >>>> <valentinium at gmail.com> wrote: >> >>>>> >> >>>>> To put this another, much briefer way, here’s a hypothetical model >> >>>>> for >> >>>>> associating private state with objects that would cover me. Privacy >> >>>>> would be >> >>>>> provided... >> >>>>> >> >>>>> 1. in the form of symbolic keys whose presence cannot be observed >> >>>>> (i.e., they would not be exposed by `getOwnPropertySymbols`) >> >>>>> 2. and which have a syntactic declaration so that one can be sure >> >>>>> they >> >>>>> are really getting private keys (i.e., an api like >> >>>>> `Symbol.private()` >> >>>>> wouldn’t work) >> >>>>> >> >>>>> ``` >> >>>>> const bar = private(); >> >>>>> >> >>>>> // alternatively: const #bar; could be anything so long as it’s >> >>>>> syntactic >> >>>>> >> >>>>> class Foo { >> >>>>> constructor() { >> >>>>> this[bar] = 1; >> >>>>> } >> >>>>> } >> >>>>> >> >>>>> // etc >> >>>>> ``` >> >>>>> >> >>>>> The keys would be typeof 'symbol'; the only difference being that >> >>>>> they >> >>>>> are symbols which are flagged as private when created. They would be >> >>>>> permitted only in syntactic property assignments and accesses. >> >>>>> Existing >> >>>>> reflection utilities would disallow the use or appearance of such >> >>>>> symbols >> >>>>> both to ensure privacy and to maintain the invariant that they are >> >>>>> always >> >>>>> simple data properties: >> >>>>> >> >>>>> ```js >> >>>>> Reflect.defineProperty({}, #bar, { ... }); // throws type error >> >>>>> Object.getOwnPropertyDescriptors(someObjWithAPrivateSlot); // does >> >>>>> not >> >>>>> include it >> >>>>> foo[bar] = 2; // fine >> >>>>> ``` >> >>>>> >> >>>>> This is significantly simpler than what’s in flight both in terms of >> >>>>> syntax and mechanics, which makes me suspicious that I’m probably >> >>>>> ignoring >> >>>>> things that other people find important. However it would bring >> >>>>> parity to ES >> >>>>> objects wrt being able to implement genuinely private slots in >> >>>>> userland with >> >>>>> the same flexibility as what is done internally. >> >>>>> >> >>>>> In total, this entails a new primary expression, a boolean flag >> >>>>> associated with symbol values, and an extra step added to several >> >>>>> algorithms >> >>>>> associated with Object and Reflect. >> >>>>> _______________________________________________ >> >>>>> 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
Right, I wouldn't, but I'm concerned others would misuse it. I don't think it's a blocker though, and actually frees weakmaps from trying to fill this role.
Right, I wouldn't, but I'm concerned others would misuse it. I don't think it's a blocker though, and actually frees weakmaps from trying to fill this role. On Sunday, July 29, 2018, Isiah Meadows <isiahmeadows at gmail.com> wrote: > It will, but weak maps will still remain useful for cases when you're > semantically dealing with a key/value map. In theory, you could > implement a weak map on top of this [1], but in practice, it doesn't > always make sense to do it. A good example of this is if you are > "tagging" an object with data. If this data isn't really part of the > object itself, you shouldn't be using a private symbol for it. Another > good example is if you're doing simple caching and you need to clear > the weak map by replacing it. Using private symbols for this doesn't > really fit with the domain here, so you're more likely just to confuse > future readers (including yourself) if you do this. > > [1]: https://gist.github.com/isiahmeadows/a8494868c4b193dfbf7139589f472ad8 > ----- > > Isiah Meadows > me at isiahmeadows.com > www.isiahmeadows.com > > > On Sun, Jul 29, 2018 at 10:05 PM, Michael Theriot > <michael.lee.theriot at gmail.com> wrote: > > Private symbols sounds like an easy win. They would be painfully simple, > > real properties, not just variables with property imitation syntax that > > undoubtedly confuses people. With the added benefit that children can > truly > > override the base class, freedom to define private members shared across > > otherwise unrelated objects, and even injection. My only concern is that > it > > could cross into WeakMap use cases. > > > > > > On Sunday, July 29, 2018, Isiah Meadows <isiahmeadows at gmail.com> wrote: > >> > >> BTW, I came up with an alternate proposal for privacy altogether: > >> https://github.com/tc39/proposal-class-fields/issues/115 > >> > >> TL;DR: private symbols that proxies can't see and that can't be > >> enumerated. > >> ----- > >> > >> Isiah Meadows > >> me at isiahmeadows.com > >> www.isiahmeadows.com > >> > >> > >> On Sun, Jul 29, 2018 at 12:23 AM, Darien Valentine > >> <valentinium at gmail.com> wrote: > >> >> What you're essentially asking for is a violatable private field, or > as > >> >> has been described by others, a "soft private". > >> > > >> > We might have different definitions here, but I would describe what > I’m > >> > talking about as hard private. Soft private, at least as it appears to > >> > have > >> > been defined in [prior > >> > discussions](https://github.com/tc39/proposal-private- > fields/issues/33), > >> > described an avenue where symbol keyed properties were given a new > >> > syntactic > >> > form — but they were still just regular symbol keys, and therefore > could > >> > be > >> > introspected by outside agents who had not been given express > privilege > >> > to > >> > do so: > >> > > >> >> [...] the core would be that "private state" is simply (public) > >> >> symbol-named properties, with syntactic sugar for those symbols, and > >> >> possibly some kind of introspection over them [...] > >> > > >> > The thread goes on to contrast the soft model with an earlier version > of > >> > the > >> > private fields proposal seen today. The hard private example uses the > >> > class > >> > declaration as a pseudo-scope, but contrasting these two options as if > >> > they > >> > are binary is not accurate: hard private through module/function/block > >> > scope > >> > already exists, it is just difficult to work with in the context of > >> > shared > >> > prototypes — one must either use WeakMaps, technically giving > _hardness_ > >> > because of the forgeability of `global.WeakMap` / `WeakMap.prototype` > / > >> > `WeakMap.prototype.get|has|set`, or be willing to either not worry > about > >> > garbage collection or implement it manually. This could be solved for > >> > with a > >> > few rather undramatic changes, though. > >> > > >> > Notably, the first post there lists the following as a disadvantage of > >> > the > >> > soft model it describes: > >> > > >> >> Platform objects, both within ECMAScript and in embedding > environments, > >> >> contain hard private state. If a library wants to be high-fidelity > and > >> >> just > >> >> like a platform object, soft-private state does not provide this > >> >> (@domenic) > >> > > >> > ...but neither model there quite covers that use case. Platform > objects > >> > _can_ see each other’s private state (cf the `isView` example earlier, > >> > or > >> > scan the DOM API specs / Chrome source a bit to find numerous > examples). > >> > It’s only the ES layer interacting with their interfaces that cannot. > >> > > >> > Such things can be achieved with ordinary scope, which is why the > >> > WeakMap > >> > pattern has worked in practice in my experience to date, while > >> > class-declaration-scoped privacy has not. It isn’t uncommon for a > >> > library’s > >> > exposed interface to be composed of an object graph, where privacy is > a > >> > concern at this public interface level, but library internal state may > >> > be > >> > interconnected in unexposed ways under the hood. The most familiar > >> > example > >> > of this is a DOM node tree. As an experiment, perhaps try to implement > >> > the > >> > relationships between HTMLFormElement, HTMLFormControlsCollection and > >> > the > >> > various form control elements using either the main private fields > >> > proposal > >> > or your alternative proposal and see what happens. > >> > > >> >> However, the guardian logic tries to verify that the function trying > to > >> >> access the private fields of an instance is a member of the same or > >> >> descending prototype that was used to create that instance. > >> > > >> > Because I’m looking at this in terms of slots, I’d first point out > that > >> > prototypes don’t determine slottedness, the execution of some specific > >> > constructor does. It’s during this process that slots are associated > >> > with > >> > the newly minted object by its identity. But even the current private > >> > fields > >> > proposal tracks this behavior closely, and I’m not sure how else it > >> > could > >> > work. The [[Prototype]] slot of an object is typically mutable > >> > (`R|O.setPrototypeOf`, `__proto__`) and forgeable (Proxy’s > >> > `getPrototypeOf` > >> > trap). Why/how would its value matter when it comes to accessing > private > >> > state? > >> > > >> > ```js > >> > const pattern = /foo/; > >> > Reflect.setPrototypeOf(pattern, Date.prototype); > >> > pattern instanceof Date; // true > >> > pattern instanceof RegExp; // false > >> > pattern.getMinutes(); // throws TypeError because [[DateValue]] slot > is > >> > missing > >> > RegExp.prototype.exec.call(pattern, 'foo'); // works; object has > RegExp > >> > private slots > >> > ``` > >> > > >> >> If I removed that requirement, it would work. However, there'd be no > >> >> way > >> >> to keep the private data from being leaked. Sadly, it's all or > nothing > >> >> with > >> >> this approach. Hard private or soft private, those are the only > >> >> choices. > >> > > >> > In the context of what you’ve described here this may be true, but no > >> > such > >> > limitation presently exists. We can already do all this — hard, > >> > leak-free > >> > privacy, brandedness, “friends” etc — with scopes and WeakMaps, but > for > >> > the > >> > fact that the `WeakMap` intrinsics may be forged. So what’s baffled me > >> > is > >> > this: why are all the proposals exploring this space not addressing > that > >> > relatively simple existing problem, and instead starting off from a > >> > place of > >> > significant new complexity? You said “maybe after the private fields > >> > problem > >> > has been resolved, someone will figure out a better way to handle your > >> > use > >> > cases,” but I’d have hoped for the opposite — I want the primitive > >> > building > >> > blocks which things like class field syntax could be built over, if it > >> > is > >> > found that they are still necessary once the root issue is solved for. > >> > > >> >> The main reason the privacy is set on a declaration level is because > >> >> scope-level inheritance isn't very good for class-oriented > inheritance. > >> > > >> > Can you explain this more? I’m not sure what’s meant by “scope-level > >> > inheritance” here. > >> > > >> >> I don't intend to stop [...] > >> > > >> > I very much admire your dedication! I’m also digging the discussion. I > >> > think > >> > we may be representing viewpoints at opposite extremes here, so it’s > an > >> > interesting contrast, but it also probably means we may be lacking > some > >> > context for understanding one another’s angles. I’d be curious to hear > >> > more > >> > about what you see as the problems with the current fields proposal + > >> > how > >> > your members proposal would solve them; the repo readme didn’t seem to > >> > include a rationale section. > >> > > >> > On Sat, Jul 28, 2018 at 10:30 PM Ranando King <kingmph at gmail.com> > wrote: > >> >> > >> >> I've almost given up on making any significant headway in either > >> >> adjusting > >> >> or flat-out correcting the flaws in that proposal, but I don't intend > >> >> to > >> >> stop trying until either we get stuck with that proposal, or they > >> >> understand > >> >> and accept what I'm telling them, or logically prove that my concerns > >> >> are > >> >> either irrational or inconsequential. > >> >> > >> >> > Private object state in particular is only _made complex_ by > >> >> > associating > >> >> > it with declarations instead of scopes that happen to contain > >> >> > declarations > >> >> > (or into which constructors are passed, etc). The complexity is > >> >> > artificial — > >> >> > not a good sign imo. > >> >> > >> >> That's not quite right. What you're essentially asking for is a > >> >> violatable > >> >> private field, or as has been described by others, a "soft private". > >> >> Since > >> >> we agree that the "friendly" & "befriend" pair is a somewhat (if not > >> >> completely) bad idea, I'm going to take 1 more pass at your 3 > requests > >> >> with > >> >> a different angle. > >> >> > >> >> > Adding the same “slot” to multiple classes which don’t inherit from > >> >> > each > >> >> > other > >> >> > Selectively sharing access to private state through functions > >> >> > declared > >> >> > outside the class body > >> >> > >> >> ```js > >> >> //Using my proposal > >> >> var {A, B, C} = (() => { > >> >> const common = Symbol("common"); > >> >> > >> >> class A { > >> >> private [common] = 1; > >> >> add(...args) { > >> >> var retval = this#[common]; > >> >> for (let obj of args) { > >> >> retval += obj#[common]; > >> >> } > >> >> return retval; > >> >> } > >> >> } > >> >> class B { > >> >> private [common] = 2; > >> >> optional() { > >> >> console.log(`common member = ${this#[common]}`); > >> >> } > >> >> } > >> >> var C = { > >> >> private [common]: 3, > >> >> required() { > >> >> console.log(`common member = ${this#[common]}`); > >> >> } > >> >> } > >> >> > >> >> return { A, B, C }; > >> >> })(); > >> >> > >> >> //So you want the following statement to not throw a TypeError and > >> >> return > >> >> 6 > >> >> (new A()).add(new B(), C); > >> >> ``` > >> >> I'm not sure I can make this work in my proposal, and I'm absolutely > >> >> sure > >> >> you'd be flatly refused by the other proposal. If a `Symbol` is > >> >> provided as > >> >> the `[[IdentifierName]]` of a private or protected field, then I can > >> >> let > >> >> that `Symbol` be both the key and value that are added to the > >> >> `[[DeclarationInfo]]` and `[[InheritanceInfo]]` records. That way > there > >> >> will > >> >> be a common private field name usable by all 3 objects. However, the > >> >> guardian logic tries to verify that the function trying to access the > >> >> private fields of an instance is a member of the same or descending > >> >> prototype that was used to create that instance. If I removed that > >> >> requirement, it would work. However, there'd be no way to keep the > >> >> private > >> >> data from being leaked. Sadly, it's all or nothing with this > approach. > >> >> Hard > >> >> private or soft private, those are the only choices. The TC39 board > has > >> >> already decided that what they want new syntax for is hard private. > >> >> > >> >> > Adding slots dynamically, e.g. when adding mix-in methods that may > >> >> > initialize a new slot if necessary when called, since subclassing > is > >> >> > not > >> >> > always appropriate > >> >> > >> >> Because the TC39 board has set their sights on hard private, this > will > >> >> require new syntax like what I suggested earlier Adding private > members > >> >> dynamically would also pose a leak risk if it could be done after the > >> >> prototype has been fully constructed. The main reason the privacy is > >> >> set on > >> >> a declaration level is because scope-level inheritance isn't very > good > >> >> for > >> >> `class`-oriented inheritance. The `class` keyword was provided to > >> >> simplify > >> >> the vertical inheritance model, along with some API to enable > >> >> inheritance > >> >> from native objects even without using `class`. The syntax changes > for > >> >> simplifying private field declaration are just an extension of that. > >> >> Even > >> >> though it's not unusual for some developers to spend a lot of time > >> >> working > >> >> with fringe use-cases, syntax changes are almost always going to be > >> >> made for > >> >> the most common use cases first. Maybe after the private fields > problem > >> >> has > >> >> been resolved, someone will figure out a better way to handle your > use > >> >> cases. > >> >> > >> >> > >> >> On Sat, Jul 28, 2018 at 3:52 PM Darien Valentine > >> >> <valentinium at gmail.com> > >> >> wrote: > >> >>> > >> >>> > Are you saying you want multiple non-hierarchally related classes > to > >> >>> > have an instance private field with shared name [...] > >> >>> > >> >>> Yeah. This is a hard problem to solve when trying to integrate > private > >> >>> fields with class syntax, but it’s not a problem at all when privacy > >> >>> is a > >> >>> more generic tool based on scope. This also isn’t a foreign concept > in > >> >>> ES: > >> >>> consider this intrinsic method: > >> >>> > >> >>> https://tc39.github.io/ecma262/#sec-arraybuffer.isview > >> >>> > >> >>> This method returns true if the argument has the > >> >>> `[[ViewedArrayBuffer]]` > >> >>> slot. This slot exists on genuine instances of both `%TypedArray%` > and > >> >>> `%DataView%`, but they do not receive these slots by way of > >> >>> inheritance from > >> >>> a common constructor. There are similar cases in HTML host APIs. > >> >>> > >> >>> > The befriend keyword would allow an object to request friendship > >> >>> > with > >> >>> > an existing friendly object. I'm not sure this is a good idea, > >> >>> > though. > >> >>> > >> >>> I don’t think it is either, no. It’s too much complexity for too > >> >>> little > >> >>> gain. But again, this is achievable “for free” just by divorcing > >> >>> “private > >> >>> object state” from class declarations (or object literals). I would > >> >>> ask: > >> >>> what problem is solved by making this a feature of the declarations > >> >>> themselves? Does it merit the complexity and the hoop jumping needed > >> >>> to > >> >>> handle edge cases?\* > >> >>> > >> >>> \* One person’s edge case; another’s everyday concern haha. > >> >>> > >> >>> > The example you gave above still declares the functions in > question > >> >>> > inside the class body, so that's not really a solution. > >> >>> > >> >>> If you’re referring to the first example, that is a demonstration of > >> >>> what > >> >>> is possible using the existing stage 3 class fields proposal as > >> >>> implemented > >> >>> in Chrome. It isn’t what I want; it’s what’s necessary to achieve > this > >> >>> with > >> >>> the current stage 3 proposed model. > >> >>> > >> >>> > Sounds to me like you'd love for class syntax to look like this > >> >>> > [[example with mixin syntax in declaration]] > >> >>> > >> >>> Perhaps — it’s interesting for sure! But the pattern that already > >> >>> works, > >> >>> `mixin(Cstr)`, is not presently a source of problems for me. Private > >> >>> object > >> >>> state in particular is only _made complex_ by associating it with > >> >>> declarations instead of scopes that happen to contain declarations > (or > >> >>> into > >> >>> which constructors are passed, etc). The complexity is artificial — > >> >>> not a > >> >>> good sign imo. > >> >>> > >> >>> > One thing both proposal-class-fields and proposal-object-members > >> >>> > have > >> >>> > in common is that the focus is on producing instance-private > fields. > >> >>> > All 3 > >> >>> > of the scenarios you presented lay outside of that focus for one > >> >>> > reason or > >> >>> > another. > >> >>> > >> >>> Both the WeakMap solution and the stub concept I provided after are > >> >>> more > >> >>> generic than privacy in either of those proposals. When I say > "object > >> >>> private state," it’s true that the object in question could be any > >> >>> object. > >> >>> But in practice, any realization of the feature would pertain > chiefly > >> >>> to > >> >>> class instances, and the examples I gave, though contrived, do > concern > >> >>> class > >> >>> instances. The reason private object state is chiefly an issue of > >> >>> class > >> >>> instances stems directly from the nature of prototype methods and > >> >>> accessors, > >> >>> so if you are not making use of prototypes, you could instead have > >> >>> used a > >> >>> closure+factory directly. > >> >>> > >> >>> --- > >> >>> > >> >>> In a nutshell, my issue with existing proposals could probably be > >> >>> summarized as a concern that they are neither as generic nor as > simple > >> >>> as > >> >>> native slots. To be clear, proper “slots” are an internal concept, > >> >>> only > >> >>> observable indirectly — but they are the special sauce underlying a > >> >>> number > >> >>> of behaviors which are presently awkward to achieve in ES code > itself, > >> >>> and > >> >>> they are a nice simple model of private object state which is > >> >>> tantalizingly > >> >>> close to, but not _exactly_ the same as in two critical ways, symbol > >> >>> keyed > >> >>> properties. That said, “real” slots would continue to have an > >> >>> advantage with > >> >>> regard to cross-realm stuff even if private symbol keys existed. > >> >>> > >> >>> That such a model is radically simpler — minmax and all that — feels > >> >>> very > >> >>> important to me, but I dunno. I’m not holding my breath for big > >> >>> changes > >> >>> here. The current stage 3 proposal seems to be unstoppable; much > >> >>> smarter / > >> >>> more important people than me have already tried and failed. :) > >> >>> > >> >>> > >> >>> On Sat, Jul 28, 2018 at 3:14 PM Ranando King <kingmph at gmail.com> > >> >>> wrote: > >> >>>> > >> >>>> In a word... wow. You've got me thinking hard here. Those are some > >> >>>> peculiar use cases, and they do a great job of highlighting why > >> >>>> someone > >> >>>> might forego using `class`. One thing both proposal-class-fields > and > >> >>>> proposal-object-members have in common is that the focus is on > >> >>>> producing > >> >>>> instance-private fields. All 3 of the scenarios you presented lay > >> >>>> outside of > >> >>>> that focus for one reason or another. > >> >>>> > >> >>>> > Adding the same “slot” to multiple classes which don’t inherit > from > >> >>>> > each other > >> >>>> > >> >>>> I'm a little confused by this one. Are you saying you want multiple > >> >>>> non-hierarchally related classes to have an instance private field > >> >>>> with > >> >>>> shared name, such that the same private field name refers to a > >> >>>> distinct and > >> >>>> separate field on each instance of every such class, but where any > >> >>>> such > >> >>>> instance can have that field referenced by that shared name from > any > >> >>>> member > >> >>>> function of the corresponding classes? (Wow that was wordy to write > >> >>>> out...) > >> >>>> If this is what you meant, you're describing friend classes. The > >> >>>> top-down > >> >>>> processing nature of ES makes this a difficult thing to create a > >> >>>> clean > >> >>>> syntax for without risking leaking the private state or > fundamentally > >> >>>> altering how ES is processed. Mutual friendship is even harder. > >> >>>> > >> >>>> ... and yet I just thought of a way to do it. By telling you this > I'm > >> >>>> leaving myself to consider writing a proposal containing 2 new > >> >>>> keywords: > >> >>>> `befriend` and `friendly`. I don't know if this can be done with > the > >> >>>> existing proposal being what it is. However, with my proposal, > >> >>>> there's a > >> >>>> chance. The `friendly` keyword would declare that an object is > >> >>>> prepared to > >> >>>> share select information with any object that befriends it. The > >> >>>> `befriend` > >> >>>> keyword would allow an object to request friendship with an > existing > >> >>>> friendly object. I'm not sure this is a good idea, though. This > means > >> >>>> that > >> >>>> any object declared 'friendly' is automatically insecure as all it > >> >>>> takes to > >> >>>> gain access to the selected members of its private space would be > to > >> >>>> 'befriend' it. > >> >>>> > >> >>>> > Selectively sharing access to private state through functions > >> >>>> > declared > >> >>>> > outside the class body > >> >>>> > >> >>>> The example you gave above still declares the functions in question > >> >>>> inside the `class` body, so that's not really a solution. If the > >> >>>> example you > >> >>>> gave actually solves your use case, then what you're asking for > here > >> >>>> isn't > >> >>>> even needed. If, however, that was a bad example, then it sounds > like > >> >>>> you're > >> >>>> looking for friend functions. See the previous section. > >> >>>> > >> >>>> > Adding slots dynamically, e.g. when adding mix-in methods that > may > >> >>>> > initialize a new slot if necessary when called, since subclassing > >> >>>> > is not > >> >>>> > always appropriate > >> >>>> > >> >>>> Sounds to me like you'd love for `class` syntax to look like this: > >> >>>> > >> >>>> ```js > >> >>>> class [<identifierName1>] [extends <identifierName2>] [mixes > >> >>>> <identifierName3>[, <identifierName3>[, ...]]] { ... } > >> >>>> ``` > >> >>>> so that the private fields of the objects in the `mixes` list are > >> >>>> added > >> >>>> to the set of private fields provided by the `class` definition > >> >>>> directly. > >> >>>> That would also require another proposal, but I think that can be > >> >>>> done > >> >>>> regardless of which instance-private fields proposal gets accepted. > >> >>>> > >> >>>> On Sat, Jul 28, 2018 at 12:49 PM Darien Valentine > >> >>>> <valentinium at gmail.com> wrote: > >> >>>>> > >> >>>>> To put this another, much briefer way, here’s a hypothetical model > >> >>>>> for > >> >>>>> associating private state with objects that would cover me. > Privacy > >> >>>>> would be > >> >>>>> provided... > >> >>>>> > >> >>>>> 1. in the form of symbolic keys whose presence cannot be observed > >> >>>>> (i.e., they would not be exposed by `getOwnPropertySymbols`) > >> >>>>> 2. and which have a syntactic declaration so that one can be sure > >> >>>>> they > >> >>>>> are really getting private keys (i.e., an api like > >> >>>>> `Symbol.private()` > >> >>>>> wouldn’t work) > >> >>>>> > >> >>>>> ``` > >> >>>>> const bar = private(); > >> >>>>> > >> >>>>> // alternatively: const #bar; could be anything so long as it’s > >> >>>>> syntactic > >> >>>>> > >> >>>>> class Foo { > >> >>>>> constructor() { > >> >>>>> this[bar] = 1; > >> >>>>> } > >> >>>>> } > >> >>>>> > >> >>>>> // etc > >> >>>>> ``` > >> >>>>> > >> >>>>> The keys would be typeof 'symbol'; the only difference being that > >> >>>>> they > >> >>>>> are symbols which are flagged as private when created. They would > be > >> >>>>> permitted only in syntactic property assignments and accesses. > >> >>>>> Existing > >> >>>>> reflection utilities would disallow the use or appearance of such > >> >>>>> symbols > >> >>>>> both to ensure privacy and to maintain the invariant that they are > >> >>>>> always > >> >>>>> simple data properties: > >> >>>>> > >> >>>>> ```js > >> >>>>> Reflect.defineProperty({}, #bar, { ... }); // throws type error > >> >>>>> Object.getOwnPropertyDescriptors(someObjWithAPrivateSlot); // > does > >> >>>>> not > >> >>>>> include it > >> >>>>> foo[bar] = 2; // fine > >> >>>>> ``` > >> >>>>> > >> >>>>> This is significantly simpler than what’s in flight both in terms > of > >> >>>>> syntax and mechanics, which makes me suspicious that I’m probably > >> >>>>> ignoring > >> >>>>> things that other people find important. However it would bring > >> >>>>> parity to ES > >> >>>>> objects wrt being able to implement genuinely private slots in > >> >>>>> userland with > >> >>>>> the same flexibility as what is done internally. > >> >>>>> > >> >>>>> In total, this entails a new primary expression, a boolean flag > >> >>>>> associated with symbol values, and an extra step added to several > >> >>>>> algorithms > >> >>>>> associated with Object and Reflect. > >> >>>>> _______________________________________________ > >> >>>>> 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/20180729/ad2e6718/attachment-0001.html>
I'm aware it's possible to misuse, but if concerns of misuse were a serious issue, we wouldn't have iterators, for example 1, 2. But IMHO freeing weak maps from a role they weren't designed for substantially outweighs the risks of abusing them further (and the abuses are incredibly frequent).
Isiah Meadows me at isiahmeadows.com, www.isiahmeadows.com
I'm aware it's possible to misuse, but if concerns of misuse were a serious issue, we wouldn't have iterators, for example [1] [2]. But IMHO freeing weak maps from a role they weren't designed for substantially outweighs the risks of abusing them further (and the abuses are incredibly frequent). [1]: https://esdiscuss.org/topic/iterators-generators-finally-and-scarce-resources-was-april-10-2014-meeting-notes [2]: https://esdiscuss.org/topic/resource-management ----- Isiah Meadows me at isiahmeadows.com www.isiahmeadows.com On Sun, Jul 29, 2018 at 10:55 PM, Michael Theriot <michael.lee.theriot at gmail.com> wrote: > Right, I wouldn't, but I'm concerned others would misuse it. I don't think > it's a blocker though, and actually frees weakmaps from trying to fill this > role. > > > On Sunday, July 29, 2018, Isiah Meadows <isiahmeadows at gmail.com> wrote: >> >> It will, but weak maps will still remain useful for cases when you're >> semantically dealing with a key/value map. In theory, you could >> implement a weak map on top of this [1], but in practice, it doesn't >> always make sense to do it. A good example of this is if you are >> "tagging" an object with data. If this data isn't really part of the >> object itself, you shouldn't be using a private symbol for it. Another >> good example is if you're doing simple caching and you need to clear >> the weak map by replacing it. Using private symbols for this doesn't >> really fit with the domain here, so you're more likely just to confuse >> future readers (including yourself) if you do this. >> >> [1]: https://gist.github.com/isiahmeadows/a8494868c4b193dfbf7139589f472ad8 >> ----- >> >> Isiah Meadows >> me at isiahmeadows.com >> www.isiahmeadows.com >> >> >> On Sun, Jul 29, 2018 at 10:05 PM, Michael Theriot >> <michael.lee.theriot at gmail.com> wrote: >> > Private symbols sounds like an easy win. They would be painfully simple, >> > real properties, not just variables with property imitation syntax that >> > undoubtedly confuses people. With the added benefit that children can >> > truly >> > override the base class, freedom to define private members shared across >> > otherwise unrelated objects, and even injection. My only concern is that >> > it >> > could cross into WeakMap use cases. >> > >> > >> > On Sunday, July 29, 2018, Isiah Meadows <isiahmeadows at gmail.com> wrote: >> >> >> >> BTW, I came up with an alternate proposal for privacy altogether: >> >> https://github.com/tc39/proposal-class-fields/issues/115 >> >> >> >> TL;DR: private symbols that proxies can't see and that can't be >> >> enumerated. >> >> ----- >> >> >> >> Isiah Meadows >> >> me at isiahmeadows.com >> >> www.isiahmeadows.com >> >> >> >> >> >> On Sun, Jul 29, 2018 at 12:23 AM, Darien Valentine >> >> <valentinium at gmail.com> wrote: >> >> >> What you're essentially asking for is a violatable private field, or >> >> >> as >> >> >> has been described by others, a "soft private". >> >> > >> >> > We might have different definitions here, but I would describe what >> >> > I’m >> >> > talking about as hard private. Soft private, at least as it appears >> >> > to >> >> > have >> >> > been defined in [prior >> >> > >> >> > discussions](https://github.com/tc39/proposal-private-fields/issues/33), >> >> > described an avenue where symbol keyed properties were given a new >> >> > syntactic >> >> > form — but they were still just regular symbol keys, and therefore >> >> > could >> >> > be >> >> > introspected by outside agents who had not been given express >> >> > privilege >> >> > to >> >> > do so: >> >> > >> >> >> [...] the core would be that "private state" is simply (public) >> >> >> symbol-named properties, with syntactic sugar for those symbols, and >> >> >> possibly some kind of introspection over them [...] >> >> > >> >> > The thread goes on to contrast the soft model with an earlier version >> >> > of >> >> > the >> >> > private fields proposal seen today. The hard private example uses the >> >> > class >> >> > declaration as a pseudo-scope, but contrasting these two options as >> >> > if >> >> > they >> >> > are binary is not accurate: hard private through >> >> > module/function/block >> >> > scope >> >> > already exists, it is just difficult to work with in the context of >> >> > shared >> >> > prototypes — one must either use WeakMaps, technically giving >> >> > _hardness_ >> >> > because of the forgeability of `global.WeakMap` / `WeakMap.prototype` >> >> > / >> >> > `WeakMap.prototype.get|has|set`, or be willing to either not worry >> >> > about >> >> > garbage collection or implement it manually. This could be solved for >> >> > with a >> >> > few rather undramatic changes, though. >> >> > >> >> > Notably, the first post there lists the following as a disadvantage >> >> > of >> >> > the >> >> > soft model it describes: >> >> > >> >> >> Platform objects, both within ECMAScript and in embedding >> >> >> environments, >> >> >> contain hard private state. If a library wants to be high-fidelity >> >> >> and >> >> >> just >> >> >> like a platform object, soft-private state does not provide this >> >> >> (@domenic) >> >> > >> >> > ...but neither model there quite covers that use case. Platform >> >> > objects >> >> > _can_ see each other’s private state (cf the `isView` example >> >> > earlier, >> >> > or >> >> > scan the DOM API specs / Chrome source a bit to find numerous >> >> > examples). >> >> > It’s only the ES layer interacting with their interfaces that cannot. >> >> > >> >> > Such things can be achieved with ordinary scope, which is why the >> >> > WeakMap >> >> > pattern has worked in practice in my experience to date, while >> >> > class-declaration-scoped privacy has not. It isn’t uncommon for a >> >> > library’s >> >> > exposed interface to be composed of an object graph, where privacy is >> >> > a >> >> > concern at this public interface level, but library internal state >> >> > may >> >> > be >> >> > interconnected in unexposed ways under the hood. The most familiar >> >> > example >> >> > of this is a DOM node tree. As an experiment, perhaps try to >> >> > implement >> >> > the >> >> > relationships between HTMLFormElement, HTMLFormControlsCollection and >> >> > the >> >> > various form control elements using either the main private fields >> >> > proposal >> >> > or your alternative proposal and see what happens. >> >> > >> >> >> However, the guardian logic tries to verify that the function trying >> >> >> to >> >> >> access the private fields of an instance is a member of the same or >> >> >> descending prototype that was used to create that instance. >> >> > >> >> > Because I’m looking at this in terms of slots, I’d first point out >> >> > that >> >> > prototypes don’t determine slottedness, the execution of some >> >> > specific >> >> > constructor does. It’s during this process that slots are associated >> >> > with >> >> > the newly minted object by its identity. But even the current private >> >> > fields >> >> > proposal tracks this behavior closely, and I’m not sure how else it >> >> > could >> >> > work. The [[Prototype]] slot of an object is typically mutable >> >> > (`R|O.setPrototypeOf`, `__proto__`) and forgeable (Proxy’s >> >> > `getPrototypeOf` >> >> > trap). Why/how would its value matter when it comes to accessing >> >> > private >> >> > state? >> >> > >> >> > ```js >> >> > const pattern = /foo/; >> >> > Reflect.setPrototypeOf(pattern, Date.prototype); >> >> > pattern instanceof Date; // true >> >> > pattern instanceof RegExp; // false >> >> > pattern.getMinutes(); // throws TypeError because [[DateValue]] slot >> >> > is >> >> > missing >> >> > RegExp.prototype.exec.call(pattern, 'foo'); // works; object has >> >> > RegExp >> >> > private slots >> >> > ``` >> >> > >> >> >> If I removed that requirement, it would work. However, there'd be no >> >> >> way >> >> >> to keep the private data from being leaked. Sadly, it's all or >> >> >> nothing >> >> >> with >> >> >> this approach. Hard private or soft private, those are the only >> >> >> choices. >> >> > >> >> > In the context of what you’ve described here this may be true, but no >> >> > such >> >> > limitation presently exists. We can already do all this — hard, >> >> > leak-free >> >> > privacy, brandedness, “friends” etc — with scopes and WeakMaps, but >> >> > for >> >> > the >> >> > fact that the `WeakMap` intrinsics may be forged. So what’s baffled >> >> > me >> >> > is >> >> > this: why are all the proposals exploring this space not addressing >> >> > that >> >> > relatively simple existing problem, and instead starting off from a >> >> > place of >> >> > significant new complexity? You said “maybe after the private fields >> >> > problem >> >> > has been resolved, someone will figure out a better way to handle >> >> > your >> >> > use >> >> > cases,” but I’d have hoped for the opposite — I want the primitive >> >> > building >> >> > blocks which things like class field syntax could be built over, if >> >> > it >> >> > is >> >> > found that they are still necessary once the root issue is solved >> >> > for. >> >> > >> >> >> The main reason the privacy is set on a declaration level is because >> >> >> scope-level inheritance isn't very good for class-oriented >> >> >> inheritance. >> >> > >> >> > Can you explain this more? I’m not sure what’s meant by “scope-level >> >> > inheritance” here. >> >> > >> >> >> I don't intend to stop [...] >> >> > >> >> > I very much admire your dedication! I’m also digging the discussion. >> >> > I >> >> > think >> >> > we may be representing viewpoints at opposite extremes here, so it’s >> >> > an >> >> > interesting contrast, but it also probably means we may be lacking >> >> > some >> >> > context for understanding one another’s angles. I’d be curious to >> >> > hear >> >> > more >> >> > about what you see as the problems with the current fields proposal + >> >> > how >> >> > your members proposal would solve them; the repo readme didn’t seem >> >> > to >> >> > include a rationale section. >> >> > >> >> > On Sat, Jul 28, 2018 at 10:30 PM Ranando King <kingmph at gmail.com> >> >> > wrote: >> >> >> >> >> >> I've almost given up on making any significant headway in either >> >> >> adjusting >> >> >> or flat-out correcting the flaws in that proposal, but I don't >> >> >> intend >> >> >> to >> >> >> stop trying until either we get stuck with that proposal, or they >> >> >> understand >> >> >> and accept what I'm telling them, or logically prove that my >> >> >> concerns >> >> >> are >> >> >> either irrational or inconsequential. >> >> >> >> >> >> > Private object state in particular is only _made complex_ by >> >> >> > associating >> >> >> > it with declarations instead of scopes that happen to contain >> >> >> > declarations >> >> >> > (or into which constructors are passed, etc). The complexity is >> >> >> > artificial — >> >> >> > not a good sign imo. >> >> >> >> >> >> That's not quite right. What you're essentially asking for is a >> >> >> violatable >> >> >> private field, or as has been described by others, a "soft private". >> >> >> Since >> >> >> we agree that the "friendly" & "befriend" pair is a somewhat (if not >> >> >> completely) bad idea, I'm going to take 1 more pass at your 3 >> >> >> requests >> >> >> with >> >> >> a different angle. >> >> >> >> >> >> > Adding the same “slot” to multiple classes which don’t inherit >> >> >> > from >> >> >> > each >> >> >> > other >> >> >> > Selectively sharing access to private state through functions >> >> >> > declared >> >> >> > outside the class body >> >> >> >> >> >> ```js >> >> >> //Using my proposal >> >> >> var {A, B, C} = (() => { >> >> >> const common = Symbol("common"); >> >> >> >> >> >> class A { >> >> >> private [common] = 1; >> >> >> add(...args) { >> >> >> var retval = this#[common]; >> >> >> for (let obj of args) { >> >> >> retval += obj#[common]; >> >> >> } >> >> >> return retval; >> >> >> } >> >> >> } >> >> >> class B { >> >> >> private [common] = 2; >> >> >> optional() { >> >> >> console.log(`common member = ${this#[common]}`); >> >> >> } >> >> >> } >> >> >> var C = { >> >> >> private [common]: 3, >> >> >> required() { >> >> >> console.log(`common member = ${this#[common]}`); >> >> >> } >> >> >> } >> >> >> >> >> >> return { A, B, C }; >> >> >> })(); >> >> >> >> >> >> //So you want the following statement to not throw a TypeError and >> >> >> return >> >> >> 6 >> >> >> (new A()).add(new B(), C); >> >> >> ``` >> >> >> I'm not sure I can make this work in my proposal, and I'm absolutely >> >> >> sure >> >> >> you'd be flatly refused by the other proposal. If a `Symbol` is >> >> >> provided as >> >> >> the `[[IdentifierName]]` of a private or protected field, then I can >> >> >> let >> >> >> that `Symbol` be both the key and value that are added to the >> >> >> `[[DeclarationInfo]]` and `[[InheritanceInfo]]` records. That way >> >> >> there >> >> >> will >> >> >> be a common private field name usable by all 3 objects. However, the >> >> >> guardian logic tries to verify that the function trying to access >> >> >> the >> >> >> private fields of an instance is a member of the same or descending >> >> >> prototype that was used to create that instance. If I removed that >> >> >> requirement, it would work. However, there'd be no way to keep the >> >> >> private >> >> >> data from being leaked. Sadly, it's all or nothing with this >> >> >> approach. >> >> >> Hard >> >> >> private or soft private, those are the only choices. The TC39 board >> >> >> has >> >> >> already decided that what they want new syntax for is hard private. >> >> >> >> >> >> > Adding slots dynamically, e.g. when adding mix-in methods that may >> >> >> > initialize a new slot if necessary when called, since subclassing >> >> >> > is >> >> >> > not >> >> >> > always appropriate >> >> >> >> >> >> Because the TC39 board has set their sights on hard private, this >> >> >> will >> >> >> require new syntax like what I suggested earlier Adding private >> >> >> members >> >> >> dynamically would also pose a leak risk if it could be done after >> >> >> the >> >> >> prototype has been fully constructed. The main reason the privacy is >> >> >> set on >> >> >> a declaration level is because scope-level inheritance isn't very >> >> >> good >> >> >> for >> >> >> `class`-oriented inheritance. The `class` keyword was provided to >> >> >> simplify >> >> >> the vertical inheritance model, along with some API to enable >> >> >> inheritance >> >> >> from native objects even without using `class`. The syntax changes >> >> >> for >> >> >> simplifying private field declaration are just an extension of that. >> >> >> Even >> >> >> though it's not unusual for some developers to spend a lot of time >> >> >> working >> >> >> with fringe use-cases, syntax changes are almost always going to be >> >> >> made for >> >> >> the most common use cases first. Maybe after the private fields >> >> >> problem >> >> >> has >> >> >> been resolved, someone will figure out a better way to handle your >> >> >> use >> >> >> cases. >> >> >> >> >> >> >> >> >> On Sat, Jul 28, 2018 at 3:52 PM Darien Valentine >> >> >> <valentinium at gmail.com> >> >> >> wrote: >> >> >>> >> >> >>> > Are you saying you want multiple non-hierarchally related classes >> >> >>> > to >> >> >>> > have an instance private field with shared name [...] >> >> >>> >> >> >>> Yeah. This is a hard problem to solve when trying to integrate >> >> >>> private >> >> >>> fields with class syntax, but it’s not a problem at all when >> >> >>> privacy >> >> >>> is a >> >> >>> more generic tool based on scope. This also isn’t a foreign concept >> >> >>> in >> >> >>> ES: >> >> >>> consider this intrinsic method: >> >> >>> >> >> >>> https://tc39.github.io/ecma262/#sec-arraybuffer.isview >> >> >>> >> >> >>> This method returns true if the argument has the >> >> >>> `[[ViewedArrayBuffer]]` >> >> >>> slot. This slot exists on genuine instances of both `%TypedArray%` >> >> >>> and >> >> >>> `%DataView%`, but they do not receive these slots by way of >> >> >>> inheritance from >> >> >>> a common constructor. There are similar cases in HTML host APIs. >> >> >>> >> >> >>> > The befriend keyword would allow an object to request friendship >> >> >>> > with >> >> >>> > an existing friendly object. I'm not sure this is a good idea, >> >> >>> > though. >> >> >>> >> >> >>> I don’t think it is either, no. It’s too much complexity for too >> >> >>> little >> >> >>> gain. But again, this is achievable “for free” just by divorcing >> >> >>> “private >> >> >>> object state” from class declarations (or object literals). I would >> >> >>> ask: >> >> >>> what problem is solved by making this a feature of the declarations >> >> >>> themselves? Does it merit the complexity and the hoop jumping >> >> >>> needed >> >> >>> to >> >> >>> handle edge cases?\* >> >> >>> >> >> >>> \* One person’s edge case; another’s everyday concern haha. >> >> >>> >> >> >>> > The example you gave above still declares the functions in >> >> >>> > question >> >> >>> > inside the class body, so that's not really a solution. >> >> >>> >> >> >>> If you’re referring to the first example, that is a demonstration >> >> >>> of >> >> >>> what >> >> >>> is possible using the existing stage 3 class fields proposal as >> >> >>> implemented >> >> >>> in Chrome. It isn’t what I want; it’s what’s necessary to achieve >> >> >>> this >> >> >>> with >> >> >>> the current stage 3 proposed model. >> >> >>> >> >> >>> > Sounds to me like you'd love for class syntax to look like this >> >> >>> > [[example with mixin syntax in declaration]] >> >> >>> >> >> >>> Perhaps — it’s interesting for sure! But the pattern that already >> >> >>> works, >> >> >>> `mixin(Cstr)`, is not presently a source of problems for me. >> >> >>> Private >> >> >>> object >> >> >>> state in particular is only _made complex_ by associating it with >> >> >>> declarations instead of scopes that happen to contain declarations >> >> >>> (or >> >> >>> into >> >> >>> which constructors are passed, etc). The complexity is artificial — >> >> >>> not a >> >> >>> good sign imo. >> >> >>> >> >> >>> > One thing both proposal-class-fields and proposal-object-members >> >> >>> > have >> >> >>> > in common is that the focus is on producing instance-private >> >> >>> > fields. >> >> >>> > All 3 >> >> >>> > of the scenarios you presented lay outside of that focus for one >> >> >>> > reason or >> >> >>> > another. >> >> >>> >> >> >>> Both the WeakMap solution and the stub concept I provided after are >> >> >>> more >> >> >>> generic than privacy in either of those proposals. When I say >> >> >>> "object >> >> >>> private state," it’s true that the object in question could be any >> >> >>> object. >> >> >>> But in practice, any realization of the feature would pertain >> >> >>> chiefly >> >> >>> to >> >> >>> class instances, and the examples I gave, though contrived, do >> >> >>> concern >> >> >>> class >> >> >>> instances. The reason private object state is chiefly an issue of >> >> >>> class >> >> >>> instances stems directly from the nature of prototype methods and >> >> >>> accessors, >> >> >>> so if you are not making use of prototypes, you could instead have >> >> >>> used a >> >> >>> closure+factory directly. >> >> >>> >> >> >>> --- >> >> >>> >> >> >>> In a nutshell, my issue with existing proposals could probably be >> >> >>> summarized as a concern that they are neither as generic nor as >> >> >>> simple >> >> >>> as >> >> >>> native slots. To be clear, proper “slots” are an internal concept, >> >> >>> only >> >> >>> observable indirectly — but they are the special sauce underlying a >> >> >>> number >> >> >>> of behaviors which are presently awkward to achieve in ES code >> >> >>> itself, >> >> >>> and >> >> >>> they are a nice simple model of private object state which is >> >> >>> tantalizingly >> >> >>> close to, but not _exactly_ the same as in two critical ways, >> >> >>> symbol >> >> >>> keyed >> >> >>> properties. That said, “real” slots would continue to have an >> >> >>> advantage with >> >> >>> regard to cross-realm stuff even if private symbol keys existed. >> >> >>> >> >> >>> That such a model is radically simpler — minmax and all that — >> >> >>> feels >> >> >>> very >> >> >>> important to me, but I dunno. I’m not holding my breath for big >> >> >>> changes >> >> >>> here. The current stage 3 proposal seems to be unstoppable; much >> >> >>> smarter / >> >> >>> more important people than me have already tried and failed. :) >> >> >>> >> >> >>> >> >> >>> On Sat, Jul 28, 2018 at 3:14 PM Ranando King <kingmph at gmail.com> >> >> >>> wrote: >> >> >>>> >> >> >>>> In a word... wow. You've got me thinking hard here. Those are some >> >> >>>> peculiar use cases, and they do a great job of highlighting why >> >> >>>> someone >> >> >>>> might forego using `class`. One thing both proposal-class-fields >> >> >>>> and >> >> >>>> proposal-object-members have in common is that the focus is on >> >> >>>> producing >> >> >>>> instance-private fields. All 3 of the scenarios you presented lay >> >> >>>> outside of >> >> >>>> that focus for one reason or another. >> >> >>>> >> >> >>>> > Adding the same “slot” to multiple classes which don’t inherit >> >> >>>> > from >> >> >>>> > each other >> >> >>>> >> >> >>>> I'm a little confused by this one. Are you saying you want >> >> >>>> multiple >> >> >>>> non-hierarchally related classes to have an instance private field >> >> >>>> with >> >> >>>> shared name, such that the same private field name refers to a >> >> >>>> distinct and >> >> >>>> separate field on each instance of every such class, but where any >> >> >>>> such >> >> >>>> instance can have that field referenced by that shared name from >> >> >>>> any >> >> >>>> member >> >> >>>> function of the corresponding classes? (Wow that was wordy to >> >> >>>> write >> >> >>>> out...) >> >> >>>> If this is what you meant, you're describing friend classes. The >> >> >>>> top-down >> >> >>>> processing nature of ES makes this a difficult thing to create a >> >> >>>> clean >> >> >>>> syntax for without risking leaking the private state or >> >> >>>> fundamentally >> >> >>>> altering how ES is processed. Mutual friendship is even harder. >> >> >>>> >> >> >>>> ... and yet I just thought of a way to do it. By telling you this >> >> >>>> I'm >> >> >>>> leaving myself to consider writing a proposal containing 2 new >> >> >>>> keywords: >> >> >>>> `befriend` and `friendly`. I don't know if this can be done with >> >> >>>> the >> >> >>>> existing proposal being what it is. However, with my proposal, >> >> >>>> there's a >> >> >>>> chance. The `friendly` keyword would declare that an object is >> >> >>>> prepared to >> >> >>>> share select information with any object that befriends it. The >> >> >>>> `befriend` >> >> >>>> keyword would allow an object to request friendship with an >> >> >>>> existing >> >> >>>> friendly object. I'm not sure this is a good idea, though. This >> >> >>>> means >> >> >>>> that >> >> >>>> any object declared 'friendly' is automatically insecure as all it >> >> >>>> takes to >> >> >>>> gain access to the selected members of its private space would be >> >> >>>> to >> >> >>>> 'befriend' it. >> >> >>>> >> >> >>>> > Selectively sharing access to private state through functions >> >> >>>> > declared >> >> >>>> > outside the class body >> >> >>>> >> >> >>>> The example you gave above still declares the functions in >> >> >>>> question >> >> >>>> inside the `class` body, so that's not really a solution. If the >> >> >>>> example you >> >> >>>> gave actually solves your use case, then what you're asking for >> >> >>>> here >> >> >>>> isn't >> >> >>>> even needed. If, however, that was a bad example, then it sounds >> >> >>>> like >> >> >>>> you're >> >> >>>> looking for friend functions. See the previous section. >> >> >>>> >> >> >>>> > Adding slots dynamically, e.g. when adding mix-in methods that >> >> >>>> > may >> >> >>>> > initialize a new slot if necessary when called, since >> >> >>>> > subclassing >> >> >>>> > is not >> >> >>>> > always appropriate >> >> >>>> >> >> >>>> Sounds to me like you'd love for `class` syntax to look like this: >> >> >>>> >> >> >>>> ```js >> >> >>>> class [<identifierName1>] [extends <identifierName2>] [mixes >> >> >>>> <identifierName3>[, <identifierName3>[, ...]]] { ... } >> >> >>>> ``` >> >> >>>> so that the private fields of the objects in the `mixes` list are >> >> >>>> added >> >> >>>> to the set of private fields provided by the `class` definition >> >> >>>> directly. >> >> >>>> That would also require another proposal, but I think that can be >> >> >>>> done >> >> >>>> regardless of which instance-private fields proposal gets >> >> >>>> accepted. >> >> >>>> >> >> >>>> On Sat, Jul 28, 2018 at 12:49 PM Darien Valentine >> >> >>>> <valentinium at gmail.com> wrote: >> >> >>>>> >> >> >>>>> To put this another, much briefer way, here’s a hypothetical >> >> >>>>> model >> >> >>>>> for >> >> >>>>> associating private state with objects that would cover me. >> >> >>>>> Privacy >> >> >>>>> would be >> >> >>>>> provided... >> >> >>>>> >> >> >>>>> 1. in the form of symbolic keys whose presence cannot be observed >> >> >>>>> (i.e., they would not be exposed by `getOwnPropertySymbols`) >> >> >>>>> 2. and which have a syntactic declaration so that one can be sure >> >> >>>>> they >> >> >>>>> are really getting private keys (i.e., an api like >> >> >>>>> `Symbol.private()` >> >> >>>>> wouldn’t work) >> >> >>>>> >> >> >>>>> ``` >> >> >>>>> const bar = private(); >> >> >>>>> >> >> >>>>> // alternatively: const #bar; could be anything so long as it’s >> >> >>>>> syntactic >> >> >>>>> >> >> >>>>> class Foo { >> >> >>>>> constructor() { >> >> >>>>> this[bar] = 1; >> >> >>>>> } >> >> >>>>> } >> >> >>>>> >> >> >>>>> // etc >> >> >>>>> ``` >> >> >>>>> >> >> >>>>> The keys would be typeof 'symbol'; the only difference being that >> >> >>>>> they >> >> >>>>> are symbols which are flagged as private when created. They would >> >> >>>>> be >> >> >>>>> permitted only in syntactic property assignments and accesses. >> >> >>>>> Existing >> >> >>>>> reflection utilities would disallow the use or appearance of such >> >> >>>>> symbols >> >> >>>>> both to ensure privacy and to maintain the invariant that they >> >> >>>>> are >> >> >>>>> always >> >> >>>>> simple data properties: >> >> >>>>> >> >> >>>>> ```js >> >> >>>>> Reflect.defineProperty({}, #bar, { ... }); // throws type error >> >> >>>>> Object.getOwnPropertyDescriptors(someObjWithAPrivateSlot); // >> >> >>>>> does >> >> >>>>> not >> >> >>>>> include it >> >> >>>>> foo[bar] = 2; // fine >> >> >>>>> ``` >> >> >>>>> >> >> >>>>> This is significantly simpler than what’s in flight both in terms >> >> >>>>> of >> >> >>>>> syntax and mechanics, which makes me suspicious that I’m probably >> >> >>>>> ignoring >> >> >>>>> things that other people find important. However it would bring >> >> >>>>> parity to ES >> >> >>>>> objects wrt being able to implement genuinely private slots in >> >> >>>>> userland with >> >> >>>>> the same flexibility as what is done internally. >> >> >>>>> >> >> >>>>> In total, this entails a new primary expression, a boolean flag >> >> >>>>> associated with symbol values, and an extra step added to several >> >> >>>>> algorithms >> >> >>>>> associated with Object and Reflect. >> >> >>>>> _______________________________________________ >> >> >>>>> 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
Also throwing this out there, symbols would now carry additional information: private or normal. Would it be better to configure this on objects instead?
E.g. Object.setPropertySymbolVisibility(object, symbol, true / false)
(and then ideally sugar for this)
That way a symbol's visibility on an object is information held on the object rather than the primitive. A little more work involved, but lines up with Object.defineProperty and symbols remain purely unique identifiers.
Also throwing this out there, symbols would now carry additional information: private or normal. Would it be better to configure this on objects instead? E.g. `Object.setPropertySymbolVisibility(object, symbol, true / false)` (and then ideally sugar for this) That way a symbol's visibility on an object is information held on the object rather than the primitive. A little more work involved, but lines up with Object.defineProperty and symbols remain purely unique identifiers. On Monday, July 30, 2018, Isiah Meadows <isiahmeadows at gmail.com> wrote: > I'm aware it's possible to misuse, but if concerns of misuse were a > serious issue, we wouldn't have iterators, for example [1] [2]. But > IMHO freeing weak maps from a role they weren't designed for > substantially outweighs the risks of abusing them further (and the > abuses are incredibly frequent). > > [1]: https://esdiscuss.org/topic/iterators-generators-finally- > and-scarce-resources-was-april-10-2014-meeting-notes > [2]: https://esdiscuss.org/topic/resource-management > > ----- > > Isiah Meadows > me at isiahmeadows.com > www.isiahmeadows.com > > > On Sun, Jul 29, 2018 at 10:55 PM, Michael Theriot > <michael.lee.theriot at gmail.com> wrote: > > Right, I wouldn't, but I'm concerned others would misuse it. I don't > think > > it's a blocker though, and actually frees weakmaps from trying to fill > this > > role. > > > > > > On Sunday, July 29, 2018, Isiah Meadows <isiahmeadows at gmail.com> wrote: > >> > >> It will, but weak maps will still remain useful for cases when you're > >> semantically dealing with a key/value map. In theory, you could > >> implement a weak map on top of this [1], but in practice, it doesn't > >> always make sense to do it. A good example of this is if you are > >> "tagging" an object with data. If this data isn't really part of the > >> object itself, you shouldn't be using a private symbol for it. Another > >> good example is if you're doing simple caching and you need to clear > >> the weak map by replacing it. Using private symbols for this doesn't > >> really fit with the domain here, so you're more likely just to confuse > >> future readers (including yourself) if you do this. > >> > >> [1]: https://gist.github.com/isiahmeadows/ > a8494868c4b193dfbf7139589f472ad8 > >> ----- > >> > >> Isiah Meadows > >> me at isiahmeadows.com > >> www.isiahmeadows.com > >> > >> > >> On Sun, Jul 29, 2018 at 10:05 PM, Michael Theriot > >> <michael.lee.theriot at gmail.com> wrote: > >> > Private symbols sounds like an easy win. They would be painfully > simple, > >> > real properties, not just variables with property imitation syntax > that > >> > undoubtedly confuses people. With the added benefit that children can > >> > truly > >> > override the base class, freedom to define private members shared > across > >> > otherwise unrelated objects, and even injection. My only concern is > that > >> > it > >> > could cross into WeakMap use cases. > >> > > >> > > >> > On Sunday, July 29, 2018, Isiah Meadows <isiahmeadows at gmail.com> > wrote: > >> >> > >> >> BTW, I came up with an alternate proposal for privacy altogether: > >> >> https://github.com/tc39/proposal-class-fields/issues/115 > >> >> > >> >> TL;DR: private symbols that proxies can't see and that can't be > >> >> enumerated. > >> >> ----- > >> >> > >> >> Isiah Meadows > >> >> me at isiahmeadows.com > >> >> www.isiahmeadows.com > >> >> > >> >> > >> >> On Sun, Jul 29, 2018 at 12:23 AM, Darien Valentine > >> >> <valentinium at gmail.com> wrote: > >> >> >> What you're essentially asking for is a violatable private field, > or > >> >> >> as > >> >> >> has been described by others, a "soft private". > >> >> > > >> >> > We might have different definitions here, but I would describe what > >> >> > I’m > >> >> > talking about as hard private. Soft private, at least as it appears > >> >> > to > >> >> > have > >> >> > been defined in [prior > >> >> > > >> >> > discussions](https://github.com/tc39/proposal-private- > fields/issues/33), > >> >> > described an avenue where symbol keyed properties were given a new > >> >> > syntactic > >> >> > form — but they were still just regular symbol keys, and therefore > >> >> > could > >> >> > be > >> >> > introspected by outside agents who had not been given express > >> >> > privilege > >> >> > to > >> >> > do so: > >> >> > > >> >> >> [...] the core would be that "private state" is simply (public) > >> >> >> symbol-named properties, with syntactic sugar for those symbols, > and > >> >> >> possibly some kind of introspection over them [...] > >> >> > > >> >> > The thread goes on to contrast the soft model with an earlier > version > >> >> > of > >> >> > the > >> >> > private fields proposal seen today. The hard private example uses > the > >> >> > class > >> >> > declaration as a pseudo-scope, but contrasting these two options as > >> >> > if > >> >> > they > >> >> > are binary is not accurate: hard private through > >> >> > module/function/block > >> >> > scope > >> >> > already exists, it is just difficult to work with in the context of > >> >> > shared > >> >> > prototypes — one must either use WeakMaps, technically giving > >> >> > _hardness_ > >> >> > because of the forgeability of `global.WeakMap` / > `WeakMap.prototype` > >> >> > / > >> >> > `WeakMap.prototype.get|has|set`, or be willing to either not worry > >> >> > about > >> >> > garbage collection or implement it manually. This could be solved > for > >> >> > with a > >> >> > few rather undramatic changes, though. > >> >> > > >> >> > Notably, the first post there lists the following as a disadvantage > >> >> > of > >> >> > the > >> >> > soft model it describes: > >> >> > > >> >> >> Platform objects, both within ECMAScript and in embedding > >> >> >> environments, > >> >> >> contain hard private state. If a library wants to be high-fidelity > >> >> >> and > >> >> >> just > >> >> >> like a platform object, soft-private state does not provide this > >> >> >> (@domenic) > >> >> > > >> >> > ...but neither model there quite covers that use case. Platform > >> >> > objects > >> >> > _can_ see each other’s private state (cf the `isView` example > >> >> > earlier, > >> >> > or > >> >> > scan the DOM API specs / Chrome source a bit to find numerous > >> >> > examples). > >> >> > It’s only the ES layer interacting with their interfaces that > cannot. > >> >> > > >> >> > Such things can be achieved with ordinary scope, which is why the > >> >> > WeakMap > >> >> > pattern has worked in practice in my experience to date, while > >> >> > class-declaration-scoped privacy has not. It isn’t uncommon for a > >> >> > library’s > >> >> > exposed interface to be composed of an object graph, where privacy > is > >> >> > a > >> >> > concern at this public interface level, but library internal state > >> >> > may > >> >> > be > >> >> > interconnected in unexposed ways under the hood. The most familiar > >> >> > example > >> >> > of this is a DOM node tree. As an experiment, perhaps try to > >> >> > implement > >> >> > the > >> >> > relationships between HTMLFormElement, HTMLFormControlsCollection > and > >> >> > the > >> >> > various form control elements using either the main private fields > >> >> > proposal > >> >> > or your alternative proposal and see what happens. > >> >> > > >> >> >> However, the guardian logic tries to verify that the function > trying > >> >> >> to > >> >> >> access the private fields of an instance is a member of the same > or > >> >> >> descending prototype that was used to create that instance. > >> >> > > >> >> > Because I’m looking at this in terms of slots, I’d first point out > >> >> > that > >> >> > prototypes don’t determine slottedness, the execution of some > >> >> > specific > >> >> > constructor does. It’s during this process that slots are > associated > >> >> > with > >> >> > the newly minted object by its identity. But even the current > private > >> >> > fields > >> >> > proposal tracks this behavior closely, and I’m not sure how else it > >> >> > could > >> >> > work. The [[Prototype]] slot of an object is typically mutable > >> >> > (`R|O.setPrototypeOf`, `__proto__`) and forgeable (Proxy’s > >> >> > `getPrototypeOf` > >> >> > trap). Why/how would its value matter when it comes to accessing > >> >> > private > >> >> > state? > >> >> > > >> >> > ```js > >> >> > const pattern = /foo/; > >> >> > Reflect.setPrototypeOf(pattern, Date.prototype); > >> >> > pattern instanceof Date; // true > >> >> > pattern instanceof RegExp; // false > >> >> > pattern.getMinutes(); // throws TypeError because [[DateValue]] > slot > >> >> > is > >> >> > missing > >> >> > RegExp.prototype.exec.call(pattern, 'foo'); // works; object has > >> >> > RegExp > >> >> > private slots > >> >> > ``` > >> >> > > >> >> >> If I removed that requirement, it would work. However, there'd be > no > >> >> >> way > >> >> >> to keep the private data from being leaked. Sadly, it's all or > >> >> >> nothing > >> >> >> with > >> >> >> this approach. Hard private or soft private, those are the only > >> >> >> choices. > >> >> > > >> >> > In the context of what you’ve described here this may be true, but > no > >> >> > such > >> >> > limitation presently exists. We can already do all this — hard, > >> >> > leak-free > >> >> > privacy, brandedness, “friends” etc — with scopes and WeakMaps, but > >> >> > for > >> >> > the > >> >> > fact that the `WeakMap` intrinsics may be forged. So what’s baffled > >> >> > me > >> >> > is > >> >> > this: why are all the proposals exploring this space not addressing > >> >> > that > >> >> > relatively simple existing problem, and instead starting off from a > >> >> > place of > >> >> > significant new complexity? You said “maybe after the private > fields > >> >> > problem > >> >> > has been resolved, someone will figure out a better way to handle > >> >> > your > >> >> > use > >> >> > cases,” but I’d have hoped for the opposite — I want the primitive > >> >> > building > >> >> > blocks which things like class field syntax could be built over, if > >> >> > it > >> >> > is > >> >> > found that they are still necessary once the root issue is solved > >> >> > for. > >> >> > > >> >> >> The main reason the privacy is set on a declaration level is > because > >> >> >> scope-level inheritance isn't very good for class-oriented > >> >> >> inheritance. > >> >> > > >> >> > Can you explain this more? I’m not sure what’s meant by > “scope-level > >> >> > inheritance” here. > >> >> > > >> >> >> I don't intend to stop [...] > >> >> > > >> >> > I very much admire your dedication! I’m also digging the > discussion. > >> >> > I > >> >> > think > >> >> > we may be representing viewpoints at opposite extremes here, so > it’s > >> >> > an > >> >> > interesting contrast, but it also probably means we may be lacking > >> >> > some > >> >> > context for understanding one another’s angles. I’d be curious to > >> >> > hear > >> >> > more > >> >> > about what you see as the problems with the current fields > proposal + > >> >> > how > >> >> > your members proposal would solve them; the repo readme didn’t seem > >> >> > to > >> >> > include a rationale section. > >> >> > > >> >> > On Sat, Jul 28, 2018 at 10:30 PM Ranando King <kingmph at gmail.com> > >> >> > wrote: > >> >> >> > >> >> >> I've almost given up on making any significant headway in either > >> >> >> adjusting > >> >> >> or flat-out correcting the flaws in that proposal, but I don't > >> >> >> intend > >> >> >> to > >> >> >> stop trying until either we get stuck with that proposal, or they > >> >> >> understand > >> >> >> and accept what I'm telling them, or logically prove that my > >> >> >> concerns > >> >> >> are > >> >> >> either irrational or inconsequential. > >> >> >> > >> >> >> > Private object state in particular is only _made complex_ by > >> >> >> > associating > >> >> >> > it with declarations instead of scopes that happen to contain > >> >> >> > declarations > >> >> >> > (or into which constructors are passed, etc). The complexity is > >> >> >> > artificial — > >> >> >> > not a good sign imo. > >> >> >> > >> >> >> That's not quite right. What you're essentially asking for is a > >> >> >> violatable > >> >> >> private field, or as has been described by others, a "soft > private". > >> >> >> Since > >> >> >> we agree that the "friendly" & "befriend" pair is a somewhat (if > not > >> >> >> completely) bad idea, I'm going to take 1 more pass at your 3 > >> >> >> requests > >> >> >> with > >> >> >> a different angle. > >> >> >> > >> >> >> > Adding the same “slot” to multiple classes which don’t inherit > >> >> >> > from > >> >> >> > each > >> >> >> > other > >> >> >> > Selectively sharing access to private state through functions > >> >> >> > declared > >> >> >> > outside the class body > >> >> >> > >> >> >> ```js > >> >> >> //Using my proposal > >> >> >> var {A, B, C} = (() => { > >> >> >> const common = Symbol("common"); > >> >> >> > >> >> >> class A { > >> >> >> private [common] = 1; > >> >> >> add(...args) { > >> >> >> var retval = this#[common]; > >> >> >> for (let obj of args) { > >> >> >> retval += obj#[common]; > >> >> >> } > >> >> >> return retval; > >> >> >> } > >> >> >> } > >> >> >> class B { > >> >> >> private [common] = 2; > >> >> >> optional() { > >> >> >> console.log(`common member = ${this#[common]}`); > >> >> >> } > >> >> >> } > >> >> >> var C = { > >> >> >> private [common]: 3, > >> >> >> required() { > >> >> >> console.log(`common member = ${this#[common]}`); > >> >> >> } > >> >> >> } > >> >> >> > >> >> >> return { A, B, C }; > >> >> >> })(); > >> >> >> > >> >> >> //So you want the following statement to not throw a TypeError and > >> >> >> return > >> >> >> 6 > >> >> >> (new A()).add(new B(), C); > >> >> >> ``` > >> >> >> I'm not sure I can make this work in my proposal, and I'm > absolutely > >> >> >> sure > >> >> >> you'd be flatly refused by the other proposal. If a `Symbol` is > >> >> >> provided as > >> >> >> the `[[IdentifierName]]` of a private or protected field, then I > can > >> >> >> let > >> >> >> that `Symbol` be both the key and value that are added to the > >> >> >> `[[DeclarationInfo]]` and `[[InheritanceInfo]]` records. That way > >> >> >> there > >> >> >> will > >> >> >> be a common private field name usable by all 3 objects. However, > the > >> >> >> guardian logic tries to verify that the function trying to access > >> >> >> the > >> >> >> private fields of an instance is a member of the same or > descending > >> >> >> prototype that was used to create that instance. If I removed that > >> >> >> requirement, it would work. However, there'd be no way to keep the > >> >> >> private > >> >> >> data from being leaked. Sadly, it's all or nothing with this > >> >> >> approach. > >> >> >> Hard > >> >> >> private or soft private, those are the only choices. The TC39 > board > >> >> >> has > >> >> >> already decided that what they want new syntax for is hard > private. > >> >> >> > >> >> >> > Adding slots dynamically, e.g. when adding mix-in methods that > may > >> >> >> > initialize a new slot if necessary when called, since > subclassing > >> >> >> > is > >> >> >> > not > >> >> >> > always appropriate > >> >> >> > >> >> >> Because the TC39 board has set their sights on hard private, this > >> >> >> will > >> >> >> require new syntax like what I suggested earlier Adding private > >> >> >> members > >> >> >> dynamically would also pose a leak risk if it could be done after > >> >> >> the > >> >> >> prototype has been fully constructed. The main reason the privacy > is > >> >> >> set on > >> >> >> a declaration level is because scope-level inheritance isn't very > >> >> >> good > >> >> >> for > >> >> >> `class`-oriented inheritance. The `class` keyword was provided to > >> >> >> simplify > >> >> >> the vertical inheritance model, along with some API to enable > >> >> >> inheritance > >> >> >> from native objects even without using `class`. The syntax changes > >> >> >> for > >> >> >> simplifying private field declaration are just an extension of > that. > >> >> >> Even > >> >> >> though it's not unusual for some developers to spend a lot of time > >> >> >> working > >> >> >> with fringe use-cases, syntax changes are almost always going to > be > >> >> >> made for > >> >> >> the most common use cases first. Maybe after the private fields > >> >> >> problem > >> >> >> has > >> >> >> been resolved, someone will figure out a better way to handle your > >> >> >> use > >> >> >> cases. > >> >> >> > >> >> >> > >> >> >> On Sat, Jul 28, 2018 at 3:52 PM Darien Valentine > >> >> >> <valentinium at gmail.com> > >> >> >> wrote: > >> >> >>> > >> >> >>> > Are you saying you want multiple non-hierarchally related > classes > >> >> >>> > to > >> >> >>> > have an instance private field with shared name [...] > >> >> >>> > >> >> >>> Yeah. This is a hard problem to solve when trying to integrate > >> >> >>> private > >> >> >>> fields with class syntax, but it’s not a problem at all when > >> >> >>> privacy > >> >> >>> is a > >> >> >>> more generic tool based on scope. This also isn’t a foreign > concept > >> >> >>> in > >> >> >>> ES: > >> >> >>> consider this intrinsic method: > >> >> >>> > >> >> >>> https://tc39.github.io/ecma262/#sec-arraybuffer.isview > >> >> >>> > >> >> >>> This method returns true if the argument has the > >> >> >>> `[[ViewedArrayBuffer]]` > >> >> >>> slot. This slot exists on genuine instances of both > `%TypedArray%` > >> >> >>> and > >> >> >>> `%DataView%`, but they do not receive these slots by way of > >> >> >>> inheritance from > >> >> >>> a common constructor. There are similar cases in HTML host APIs. > >> >> >>> > >> >> >>> > The befriend keyword would allow an object to request > friendship > >> >> >>> > with > >> >> >>> > an existing friendly object. I'm not sure this is a good idea, > >> >> >>> > though. > >> >> >>> > >> >> >>> I don’t think it is either, no. It’s too much complexity for too > >> >> >>> little > >> >> >>> gain. But again, this is achievable “for free” just by divorcing > >> >> >>> “private > >> >> >>> object state” from class declarations (or object literals). I > would > >> >> >>> ask: > >> >> >>> what problem is solved by making this a feature of the > declarations > >> >> >>> themselves? Does it merit the complexity and the hoop jumping > >> >> >>> needed > >> >> >>> to > >> >> >>> handle edge cases?\* > >> >> >>> > >> >> >>> \* One person’s edge case; another’s everyday concern haha. > >> >> >>> > >> >> >>> > The example you gave above still declares the functions in > >> >> >>> > question > >> >> >>> > inside the class body, so that's not really a solution. > >> >> >>> > >> >> >>> If you’re referring to the first example, that is a demonstration > >> >> >>> of > >> >> >>> what > >> >> >>> is possible using the existing stage 3 class fields proposal as > >> >> >>> implemented > >> >> >>> in Chrome. It isn’t what I want; it’s what’s necessary to achieve > >> >> >>> this > >> >> >>> with > >> >> >>> the current stage 3 proposed model. > >> >> >>> > >> >> >>> > Sounds to me like you'd love for class syntax to look like this > >> >> >>> > [[example with mixin syntax in declaration]] > >> >> >>> > >> >> >>> Perhaps — it’s interesting for sure! But the pattern that already > >> >> >>> works, > >> >> >>> `mixin(Cstr)`, is not presently a source of problems for me. > >> >> >>> Private > >> >> >>> object > >> >> >>> state in particular is only _made complex_ by associating it with > >> >> >>> declarations instead of scopes that happen to contain > declarations > >> >> >>> (or > >> >> >>> into > >> >> >>> which constructors are passed, etc). The complexity is > artificial — > >> >> >>> not a > >> >> >>> good sign imo. > >> >> >>> > >> >> >>> > One thing both proposal-class-fields and > proposal-object-members > >> >> >>> > have > >> >> >>> > in common is that the focus is on producing instance-private > >> >> >>> > fields. > >> >> >>> > All 3 > >> >> >>> > of the scenarios you presented lay outside of that focus for > one > >> >> >>> > reason or > >> >> >>> > another. > >> >> >>> > >> >> >>> Both the WeakMap solution and the stub concept I provided after > are > >> >> >>> more > >> >> >>> generic than privacy in either of those proposals. When I say > >> >> >>> "object > >> >> >>> private state," it’s true that the object in question could be > any > >> >> >>> object. > >> >> >>> But in practice, any realization of the feature would pertain > >> >> >>> chiefly > >> >> >>> to > >> >> >>> class instances, and the examples I gave, though contrived, do > >> >> >>> concern > >> >> >>> class > >> >> >>> instances. The reason private object state is chiefly an issue of > >> >> >>> class > >> >> >>> instances stems directly from the nature of prototype methods and > >> >> >>> accessors, > >> >> >>> so if you are not making use of prototypes, you could instead > have > >> >> >>> used a > >> >> >>> closure+factory directly. > >> >> >>> > >> >> >>> --- > >> >> >>> > >> >> >>> In a nutshell, my issue with existing proposals could probably be > >> >> >>> summarized as a concern that they are neither as generic nor as > >> >> >>> simple > >> >> >>> as > >> >> >>> native slots. To be clear, proper “slots” are an internal > concept, > >> >> >>> only > >> >> >>> observable indirectly — but they are the special sauce > underlying a > >> >> >>> number > >> >> >>> of behaviors which are presently awkward to achieve in ES code > >> >> >>> itself, > >> >> >>> and > >> >> >>> they are a nice simple model of private object state which is > >> >> >>> tantalizingly > >> >> >>> close to, but not _exactly_ the same as in two critical ways, > >> >> >>> symbol > >> >> >>> keyed > >> >> >>> properties. That said, “real” slots would continue to have an > >> >> >>> advantage with > >> >> >>> regard to cross-realm stuff even if private symbol keys existed. > >> >> >>> > >> >> >>> That such a model is radically simpler — minmax and all that — > >> >> >>> feels > >> >> >>> very > >> >> >>> important to me, but I dunno. I’m not holding my breath for big > >> >> >>> changes > >> >> >>> here. The current stage 3 proposal seems to be unstoppable; much > >> >> >>> smarter / > >> >> >>> more important people than me have already tried and failed. :) > >> >> >>> > >> >> >>> > >> >> >>> On Sat, Jul 28, 2018 at 3:14 PM Ranando King <kingmph at gmail.com> > >> >> >>> wrote: > >> >> >>>> > >> >> >>>> In a word... wow. You've got me thinking hard here. Those are > some > >> >> >>>> peculiar use cases, and they do a great job of highlighting why > >> >> >>>> someone > >> >> >>>> might forego using `class`. One thing both proposal-class-fields > >> >> >>>> and > >> >> >>>> proposal-object-members have in common is that the focus is on > >> >> >>>> producing > >> >> >>>> instance-private fields. All 3 of the scenarios you presented > lay > >> >> >>>> outside of > >> >> >>>> that focus for one reason or another. > >> >> >>>> > >> >> >>>> > Adding the same “slot” to multiple classes which don’t inherit > >> >> >>>> > from > >> >> >>>> > each other > >> >> >>>> > >> >> >>>> I'm a little confused by this one. Are you saying you want > >> >> >>>> multiple > >> >> >>>> non-hierarchally related classes to have an instance private > field > >> >> >>>> with > >> >> >>>> shared name, such that the same private field name refers to a > >> >> >>>> distinct and > >> >> >>>> separate field on each instance of every such class, but where > any > >> >> >>>> such > >> >> >>>> instance can have that field referenced by that shared name from > >> >> >>>> any > >> >> >>>> member > >> >> >>>> function of the corresponding classes? (Wow that was wordy to > >> >> >>>> write > >> >> >>>> out...) > >> >> >>>> If this is what you meant, you're describing friend classes. The > >> >> >>>> top-down > >> >> >>>> processing nature of ES makes this a difficult thing to create a > >> >> >>>> clean > >> >> >>>> syntax for without risking leaking the private state or > >> >> >>>> fundamentally > >> >> >>>> altering how ES is processed. Mutual friendship is even harder. > >> >> >>>> > >> >> >>>> ... and yet I just thought of a way to do it. By telling you > this > >> >> >>>> I'm > >> >> >>>> leaving myself to consider writing a proposal containing 2 new > >> >> >>>> keywords: > >> >> >>>> `befriend` and `friendly`. I don't know if this can be done with > >> >> >>>> the > >> >> >>>> existing proposal being what it is. However, with my proposal, > >> >> >>>> there's a > >> >> >>>> chance. The `friendly` keyword would declare that an object is > >> >> >>>> prepared to > >> >> >>>> share select information with any object that befriends it. The > >> >> >>>> `befriend` > >> >> >>>> keyword would allow an object to request friendship with an > >> >> >>>> existing > >> >> >>>> friendly object. I'm not sure this is a good idea, though. This > >> >> >>>> means > >> >> >>>> that > >> >> >>>> any object declared 'friendly' is automatically insecure as all > it > >> >> >>>> takes to > >> >> >>>> gain access to the selected members of its private space would > be > >> >> >>>> to > >> >> >>>> 'befriend' it. > >> >> >>>> > >> >> >>>> > Selectively sharing access to private state through functions > >> >> >>>> > declared > >> >> >>>> > outside the class body > >> >> >>>> > >> >> >>>> The example you gave above still declares the functions in > >> >> >>>> question > >> >> >>>> inside the `class` body, so that's not really a solution. If the > >> >> >>>> example you > >> >> >>>> gave actually solves your use case, then what you're asking for > >> >> >>>> here > >> >> >>>> isn't > >> >> >>>> even needed. If, however, that was a bad example, then it sounds > >> >> >>>> like > >> >> >>>> you're > >> >> >>>> looking for friend functions. See the previous section. > >> >> >>>> > >> >> >>>> > Adding slots dynamically, e.g. when adding mix-in methods that > >> >> >>>> > may > >> >> >>>> > initialize a new slot if necessary when called, since > >> >> >>>> > subclassing > >> >> >>>> > is not > >> >> >>>> > always appropriate > >> >> >>>> > >> >> >>>> Sounds to me like you'd love for `class` syntax to look like > this: > >> >> >>>> > >> >> >>>> ```js > >> >> >>>> class [<identifierName1>] [extends <identifierName2>] [mixes > >> >> >>>> <identifierName3>[, <identifierName3>[, ...]]] { ... } > >> >> >>>> ``` > >> >> >>>> so that the private fields of the objects in the `mixes` list > are > >> >> >>>> added > >> >> >>>> to the set of private fields provided by the `class` definition > >> >> >>>> directly. > >> >> >>>> That would also require another proposal, but I think that can > be > >> >> >>>> done > >> >> >>>> regardless of which instance-private fields proposal gets > >> >> >>>> accepted. > >> >> >>>> > >> >> >>>> On Sat, Jul 28, 2018 at 12:49 PM Darien Valentine > >> >> >>>> <valentinium at gmail.com> wrote: > >> >> >>>>> > >> >> >>>>> To put this another, much briefer way, here’s a hypothetical > >> >> >>>>> model > >> >> >>>>> for > >> >> >>>>> associating private state with objects that would cover me. > >> >> >>>>> Privacy > >> >> >>>>> would be > >> >> >>>>> provided... > >> >> >>>>> > >> >> >>>>> 1. in the form of symbolic keys whose presence cannot be > observed > >> >> >>>>> (i.e., they would not be exposed by `getOwnPropertySymbols`) > >> >> >>>>> 2. and which have a syntactic declaration so that one can be > sure > >> >> >>>>> they > >> >> >>>>> are really getting private keys (i.e., an api like > >> >> >>>>> `Symbol.private()` > >> >> >>>>> wouldn’t work) > >> >> >>>>> > >> >> >>>>> ``` > >> >> >>>>> const bar = private(); > >> >> >>>>> > >> >> >>>>> // alternatively: const #bar; could be anything so long as it’s > >> >> >>>>> syntactic > >> >> >>>>> > >> >> >>>>> class Foo { > >> >> >>>>> constructor() { > >> >> >>>>> this[bar] = 1; > >> >> >>>>> } > >> >> >>>>> } > >> >> >>>>> > >> >> >>>>> // etc > >> >> >>>>> ``` > >> >> >>>>> > >> >> >>>>> The keys would be typeof 'symbol'; the only difference being > that > >> >> >>>>> they > >> >> >>>>> are symbols which are flagged as private when created. They > would > >> >> >>>>> be > >> >> >>>>> permitted only in syntactic property assignments and accesses. > >> >> >>>>> Existing > >> >> >>>>> reflection utilities would disallow the use or appearance of > such > >> >> >>>>> symbols > >> >> >>>>> both to ensure privacy and to maintain the invariant that they > >> >> >>>>> are > >> >> >>>>> always > >> >> >>>>> simple data properties: > >> >> >>>>> > >> >> >>>>> ```js > >> >> >>>>> Reflect.defineProperty({}, #bar, { ... }); // throws type error > >> >> >>>>> Object.getOwnPropertyDescriptors(someObjWithAPrivateSlot); // > >> >> >>>>> does > >> >> >>>>> not > >> >> >>>>> include it > >> >> >>>>> foo[bar] = 2; // fine > >> >> >>>>> ``` > >> >> >>>>> > >> >> >>>>> This is significantly simpler than what’s in flight both in > terms > >> >> >>>>> of > >> >> >>>>> syntax and mechanics, which makes me suspicious that I’m > probably > >> >> >>>>> ignoring > >> >> >>>>> things that other people find important. However it would bring > >> >> >>>>> parity to ES > >> >> >>>>> objects wrt being able to implement genuinely private slots in > >> >> >>>>> userland with > >> >> >>>>> the same flexibility as what is done internally. > >> >> >>>>> > >> >> >>>>> In total, this entails a new primary expression, a boolean flag > >> >> >>>>> associated with symbol values, and an extra step added to > several > >> >> >>>>> algorithms > >> >> >>>>> associated with Object and Reflect. > >> >> >>>>> _______________________________________________ > >> >> >>>>> 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/20180730/8ce7711b/attachment-0001.html>
Um, no. The use case is extremely limited, and that ruins a few optimizations you could otherwise make with private symbols (like caching proxy forwarding without having to bail out).
Besides, whether a symbol is private requires exactly one bit to store, so there's no real overhead with storing it on the object. Heck, if you want to optimize it better, you might choose to store that same bit on both the symbol and the object descriptor itself, and I'd expect engines to do just that - it saves a pointer dereference.
Isiah Meadows me at isiahmeadows.com, www.isiahmeadows.com
Um, no. The use case is *extremely* limited, and that ruins a few optimizations you could otherwise make with private symbols (like caching proxy forwarding without having to bail out). Besides, whether a symbol is private requires exactly one bit to store, so there's no real overhead with storing it on the object. Heck, if you want to optimize it better, you might choose to store that same bit on both the symbol and the object descriptor itself, and I'd expect engines to do just that - it saves a pointer dereference. ----- Isiah Meadows me at isiahmeadows.com www.isiahmeadows.com On Mon, Jul 30, 2018 at 1:25 AM, Michael Theriot <michael.lee.theriot at gmail.com> wrote: > Also throwing this out there, symbols would now carry additional > information: private or normal. Would it be better to configure this on > objects instead? > > E.g. `Object.setPropertySymbolVisibility(object, symbol, true / false)` > > (and then ideally sugar for this) > > That way a symbol's visibility on an object is information held on the > object rather than the primitive. A little more work involved, but lines up > with Object.defineProperty and symbols remain purely unique identifiers. > > On Monday, July 30, 2018, Isiah Meadows <isiahmeadows at gmail.com> wrote: >> >> I'm aware it's possible to misuse, but if concerns of misuse were a >> serious issue, we wouldn't have iterators, for example [1] [2]. But >> IMHO freeing weak maps from a role they weren't designed for >> substantially outweighs the risks of abusing them further (and the >> abuses are incredibly frequent). >> >> [1]: >> https://esdiscuss.org/topic/iterators-generators-finally-and-scarce-resources-was-april-10-2014-meeting-notes >> [2]: https://esdiscuss.org/topic/resource-management >> >> ----- >> >> Isiah Meadows >> me at isiahmeadows.com >> www.isiahmeadows.com >> >> >> On Sun, Jul 29, 2018 at 10:55 PM, Michael Theriot >> <michael.lee.theriot at gmail.com> wrote: >> > Right, I wouldn't, but I'm concerned others would misuse it. I don't >> > think >> > it's a blocker though, and actually frees weakmaps from trying to fill >> > this >> > role. >> > >> > >> > On Sunday, July 29, 2018, Isiah Meadows <isiahmeadows at gmail.com> wrote: >> >> >> >> It will, but weak maps will still remain useful for cases when you're >> >> semantically dealing with a key/value map. In theory, you could >> >> implement a weak map on top of this [1], but in practice, it doesn't >> >> always make sense to do it. A good example of this is if you are >> >> "tagging" an object with data. If this data isn't really part of the >> >> object itself, you shouldn't be using a private symbol for it. Another >> >> good example is if you're doing simple caching and you need to clear >> >> the weak map by replacing it. Using private symbols for this doesn't >> >> really fit with the domain here, so you're more likely just to confuse >> >> future readers (including yourself) if you do this. >> >> >> >> [1]: >> >> https://gist.github.com/isiahmeadows/a8494868c4b193dfbf7139589f472ad8 >> >> ----- >> >> >> >> Isiah Meadows >> >> me at isiahmeadows.com >> >> www.isiahmeadows.com >> >> >> >> >> >> On Sun, Jul 29, 2018 at 10:05 PM, Michael Theriot >> >> <michael.lee.theriot at gmail.com> wrote: >> >> > Private symbols sounds like an easy win. They would be painfully >> >> > simple, >> >> > real properties, not just variables with property imitation syntax >> >> > that >> >> > undoubtedly confuses people. With the added benefit that children can >> >> > truly >> >> > override the base class, freedom to define private members shared >> >> > across >> >> > otherwise unrelated objects, and even injection. My only concern is >> >> > that >> >> > it >> >> > could cross into WeakMap use cases. >> >> > >> >> > >> >> > On Sunday, July 29, 2018, Isiah Meadows <isiahmeadows at gmail.com> >> >> > wrote: >> >> >> >> >> >> BTW, I came up with an alternate proposal for privacy altogether: >> >> >> https://github.com/tc39/proposal-class-fields/issues/115 >> >> >> >> >> >> TL;DR: private symbols that proxies can't see and that can't be >> >> >> enumerated. >> >> >> ----- >> >> >> >> >> >> Isiah Meadows >> >> >> me at isiahmeadows.com >> >> >> www.isiahmeadows.com >> >> >> >> >> >> >> >> >> On Sun, Jul 29, 2018 at 12:23 AM, Darien Valentine >> >> >> <valentinium at gmail.com> wrote: >> >> >> >> What you're essentially asking for is a violatable private field, >> >> >> >> or >> >> >> >> as >> >> >> >> has been described by others, a "soft private". >> >> >> > >> >> >> > We might have different definitions here, but I would describe >> >> >> > what >> >> >> > I’m >> >> >> > talking about as hard private. Soft private, at least as it >> >> >> > appears >> >> >> > to >> >> >> > have >> >> >> > been defined in [prior >> >> >> > >> >> >> > >> >> >> > discussions](https://github.com/tc39/proposal-private-fields/issues/33), >> >> >> > described an avenue where symbol keyed properties were given a new >> >> >> > syntactic >> >> >> > form — but they were still just regular symbol keys, and therefore >> >> >> > could >> >> >> > be >> >> >> > introspected by outside agents who had not been given express >> >> >> > privilege >> >> >> > to >> >> >> > do so: >> >> >> > >> >> >> >> [...] the core would be that "private state" is simply (public) >> >> >> >> symbol-named properties, with syntactic sugar for those symbols, >> >> >> >> and >> >> >> >> possibly some kind of introspection over them [...] >> >> >> > >> >> >> > The thread goes on to contrast the soft model with an earlier >> >> >> > version >> >> >> > of >> >> >> > the >> >> >> > private fields proposal seen today. The hard private example uses >> >> >> > the >> >> >> > class >> >> >> > declaration as a pseudo-scope, but contrasting these two options >> >> >> > as >> >> >> > if >> >> >> > they >> >> >> > are binary is not accurate: hard private through >> >> >> > module/function/block >> >> >> > scope >> >> >> > already exists, it is just difficult to work with in the context >> >> >> > of >> >> >> > shared >> >> >> > prototypes — one must either use WeakMaps, technically giving >> >> >> > _hardness_ >> >> >> > because of the forgeability of `global.WeakMap` / >> >> >> > `WeakMap.prototype` >> >> >> > / >> >> >> > `WeakMap.prototype.get|has|set`, or be willing to either not worry >> >> >> > about >> >> >> > garbage collection or implement it manually. This could be solved >> >> >> > for >> >> >> > with a >> >> >> > few rather undramatic changes, though. >> >> >> > >> >> >> > Notably, the first post there lists the following as a >> >> >> > disadvantage >> >> >> > of >> >> >> > the >> >> >> > soft model it describes: >> >> >> > >> >> >> >> Platform objects, both within ECMAScript and in embedding >> >> >> >> environments, >> >> >> >> contain hard private state. If a library wants to be >> >> >> >> high-fidelity >> >> >> >> and >> >> >> >> just >> >> >> >> like a platform object, soft-private state does not provide this >> >> >> >> (@domenic) >> >> >> > >> >> >> > ...but neither model there quite covers that use case. Platform >> >> >> > objects >> >> >> > _can_ see each other’s private state (cf the `isView` example >> >> >> > earlier, >> >> >> > or >> >> >> > scan the DOM API specs / Chrome source a bit to find numerous >> >> >> > examples). >> >> >> > It’s only the ES layer interacting with their interfaces that >> >> >> > cannot. >> >> >> > >> >> >> > Such things can be achieved with ordinary scope, which is why the >> >> >> > WeakMap >> >> >> > pattern has worked in practice in my experience to date, while >> >> >> > class-declaration-scoped privacy has not. It isn’t uncommon for a >> >> >> > library’s >> >> >> > exposed interface to be composed of an object graph, where privacy >> >> >> > is >> >> >> > a >> >> >> > concern at this public interface level, but library internal state >> >> >> > may >> >> >> > be >> >> >> > interconnected in unexposed ways under the hood. The most familiar >> >> >> > example >> >> >> > of this is a DOM node tree. As an experiment, perhaps try to >> >> >> > implement >> >> >> > the >> >> >> > relationships between HTMLFormElement, HTMLFormControlsCollection >> >> >> > and >> >> >> > the >> >> >> > various form control elements using either the main private fields >> >> >> > proposal >> >> >> > or your alternative proposal and see what happens. >> >> >> > >> >> >> >> However, the guardian logic tries to verify that the function >> >> >> >> trying >> >> >> >> to >> >> >> >> access the private fields of an instance is a member of the same >> >> >> >> or >> >> >> >> descending prototype that was used to create that instance. >> >> >> > >> >> >> > Because I’m looking at this in terms of slots, I’d first point out >> >> >> > that >> >> >> > prototypes don’t determine slottedness, the execution of some >> >> >> > specific >> >> >> > constructor does. It’s during this process that slots are >> >> >> > associated >> >> >> > with >> >> >> > the newly minted object by its identity. But even the current >> >> >> > private >> >> >> > fields >> >> >> > proposal tracks this behavior closely, and I’m not sure how else >> >> >> > it >> >> >> > could >> >> >> > work. The [[Prototype]] slot of an object is typically mutable >> >> >> > (`R|O.setPrototypeOf`, `__proto__`) and forgeable (Proxy’s >> >> >> > `getPrototypeOf` >> >> >> > trap). Why/how would its value matter when it comes to accessing >> >> >> > private >> >> >> > state? >> >> >> > >> >> >> > ```js >> >> >> > const pattern = /foo/; >> >> >> > Reflect.setPrototypeOf(pattern, Date.prototype); >> >> >> > pattern instanceof Date; // true >> >> >> > pattern instanceof RegExp; // false >> >> >> > pattern.getMinutes(); // throws TypeError because [[DateValue]] >> >> >> > slot >> >> >> > is >> >> >> > missing >> >> >> > RegExp.prototype.exec.call(pattern, 'foo'); // works; object has >> >> >> > RegExp >> >> >> > private slots >> >> >> > ``` >> >> >> > >> >> >> >> If I removed that requirement, it would work. However, there'd be >> >> >> >> no >> >> >> >> way >> >> >> >> to keep the private data from being leaked. Sadly, it's all or >> >> >> >> nothing >> >> >> >> with >> >> >> >> this approach. Hard private or soft private, those are the only >> >> >> >> choices. >> >> >> > >> >> >> > In the context of what you’ve described here this may be true, but >> >> >> > no >> >> >> > such >> >> >> > limitation presently exists. We can already do all this — hard, >> >> >> > leak-free >> >> >> > privacy, brandedness, “friends” etc — with scopes and WeakMaps, >> >> >> > but >> >> >> > for >> >> >> > the >> >> >> > fact that the `WeakMap` intrinsics may be forged. So what’s >> >> >> > baffled >> >> >> > me >> >> >> > is >> >> >> > this: why are all the proposals exploring this space not >> >> >> > addressing >> >> >> > that >> >> >> > relatively simple existing problem, and instead starting off from >> >> >> > a >> >> >> > place of >> >> >> > significant new complexity? You said “maybe after the private >> >> >> > fields >> >> >> > problem >> >> >> > has been resolved, someone will figure out a better way to handle >> >> >> > your >> >> >> > use >> >> >> > cases,” but I’d have hoped for the opposite — I want the primitive >> >> >> > building >> >> >> > blocks which things like class field syntax could be built over, >> >> >> > if >> >> >> > it >> >> >> > is >> >> >> > found that they are still necessary once the root issue is solved >> >> >> > for. >> >> >> > >> >> >> >> The main reason the privacy is set on a declaration level is >> >> >> >> because >> >> >> >> scope-level inheritance isn't very good for class-oriented >> >> >> >> inheritance. >> >> >> > >> >> >> > Can you explain this more? I’m not sure what’s meant by >> >> >> > “scope-level >> >> >> > inheritance” here. >> >> >> > >> >> >> >> I don't intend to stop [...] >> >> >> > >> >> >> > I very much admire your dedication! I’m also digging the >> >> >> > discussion. >> >> >> > I >> >> >> > think >> >> >> > we may be representing viewpoints at opposite extremes here, so >> >> >> > it’s >> >> >> > an >> >> >> > interesting contrast, but it also probably means we may be lacking >> >> >> > some >> >> >> > context for understanding one another’s angles. I’d be curious to >> >> >> > hear >> >> >> > more >> >> >> > about what you see as the problems with the current fields >> >> >> > proposal + >> >> >> > how >> >> >> > your members proposal would solve them; the repo readme didn’t >> >> >> > seem >> >> >> > to >> >> >> > include a rationale section. >> >> >> > >> >> >> > On Sat, Jul 28, 2018 at 10:30 PM Ranando King <kingmph at gmail.com> >> >> >> > wrote: >> >> >> >> >> >> >> >> I've almost given up on making any significant headway in either >> >> >> >> adjusting >> >> >> >> or flat-out correcting the flaws in that proposal, but I don't >> >> >> >> intend >> >> >> >> to >> >> >> >> stop trying until either we get stuck with that proposal, or they >> >> >> >> understand >> >> >> >> and accept what I'm telling them, or logically prove that my >> >> >> >> concerns >> >> >> >> are >> >> >> >> either irrational or inconsequential. >> >> >> >> >> >> >> >> > Private object state in particular is only _made complex_ by >> >> >> >> > associating >> >> >> >> > it with declarations instead of scopes that happen to contain >> >> >> >> > declarations >> >> >> >> > (or into which constructors are passed, etc). The complexity is >> >> >> >> > artificial — >> >> >> >> > not a good sign imo. >> >> >> >> >> >> >> >> That's not quite right. What you're essentially asking for is a >> >> >> >> violatable >> >> >> >> private field, or as has been described by others, a "soft >> >> >> >> private". >> >> >> >> Since >> >> >> >> we agree that the "friendly" & "befriend" pair is a somewhat (if >> >> >> >> not >> >> >> >> completely) bad idea, I'm going to take 1 more pass at your 3 >> >> >> >> requests >> >> >> >> with >> >> >> >> a different angle. >> >> >> >> >> >> >> >> > Adding the same “slot” to multiple classes which don’t inherit >> >> >> >> > from >> >> >> >> > each >> >> >> >> > other >> >> >> >> > Selectively sharing access to private state through functions >> >> >> >> > declared >> >> >> >> > outside the class body >> >> >> >> >> >> >> >> ```js >> >> >> >> //Using my proposal >> >> >> >> var {A, B, C} = (() => { >> >> >> >> const common = Symbol("common"); >> >> >> >> >> >> >> >> class A { >> >> >> >> private [common] = 1; >> >> >> >> add(...args) { >> >> >> >> var retval = this#[common]; >> >> >> >> for (let obj of args) { >> >> >> >> retval += obj#[common]; >> >> >> >> } >> >> >> >> return retval; >> >> >> >> } >> >> >> >> } >> >> >> >> class B { >> >> >> >> private [common] = 2; >> >> >> >> optional() { >> >> >> >> console.log(`common member = ${this#[common]}`); >> >> >> >> } >> >> >> >> } >> >> >> >> var C = { >> >> >> >> private [common]: 3, >> >> >> >> required() { >> >> >> >> console.log(`common member = ${this#[common]}`); >> >> >> >> } >> >> >> >> } >> >> >> >> >> >> >> >> return { A, B, C }; >> >> >> >> })(); >> >> >> >> >> >> >> >> //So you want the following statement to not throw a TypeError >> >> >> >> and >> >> >> >> return >> >> >> >> 6 >> >> >> >> (new A()).add(new B(), C); >> >> >> >> ``` >> >> >> >> I'm not sure I can make this work in my proposal, and I'm >> >> >> >> absolutely >> >> >> >> sure >> >> >> >> you'd be flatly refused by the other proposal. If a `Symbol` is >> >> >> >> provided as >> >> >> >> the `[[IdentifierName]]` of a private or protected field, then I >> >> >> >> can >> >> >> >> let >> >> >> >> that `Symbol` be both the key and value that are added to the >> >> >> >> `[[DeclarationInfo]]` and `[[InheritanceInfo]]` records. That way >> >> >> >> there >> >> >> >> will >> >> >> >> be a common private field name usable by all 3 objects. However, >> >> >> >> the >> >> >> >> guardian logic tries to verify that the function trying to access >> >> >> >> the >> >> >> >> private fields of an instance is a member of the same or >> >> >> >> descending >> >> >> >> prototype that was used to create that instance. If I removed >> >> >> >> that >> >> >> >> requirement, it would work. However, there'd be no way to keep >> >> >> >> the >> >> >> >> private >> >> >> >> data from being leaked. Sadly, it's all or nothing with this >> >> >> >> approach. >> >> >> >> Hard >> >> >> >> private or soft private, those are the only choices. The TC39 >> >> >> >> board >> >> >> >> has >> >> >> >> already decided that what they want new syntax for is hard >> >> >> >> private. >> >> >> >> >> >> >> >> > Adding slots dynamically, e.g. when adding mix-in methods that >> >> >> >> > may >> >> >> >> > initialize a new slot if necessary when called, since >> >> >> >> > subclassing >> >> >> >> > is >> >> >> >> > not >> >> >> >> > always appropriate >> >> >> >> >> >> >> >> Because the TC39 board has set their sights on hard private, this >> >> >> >> will >> >> >> >> require new syntax like what I suggested earlier Adding private >> >> >> >> members >> >> >> >> dynamically would also pose a leak risk if it could be done after >> >> >> >> the >> >> >> >> prototype has been fully constructed. The main reason the privacy >> >> >> >> is >> >> >> >> set on >> >> >> >> a declaration level is because scope-level inheritance isn't very >> >> >> >> good >> >> >> >> for >> >> >> >> `class`-oriented inheritance. The `class` keyword was provided to >> >> >> >> simplify >> >> >> >> the vertical inheritance model, along with some API to enable >> >> >> >> inheritance >> >> >> >> from native objects even without using `class`. The syntax >> >> >> >> changes >> >> >> >> for >> >> >> >> simplifying private field declaration are just an extension of >> >> >> >> that. >> >> >> >> Even >> >> >> >> though it's not unusual for some developers to spend a lot of >> >> >> >> time >> >> >> >> working >> >> >> >> with fringe use-cases, syntax changes are almost always going to >> >> >> >> be >> >> >> >> made for >> >> >> >> the most common use cases first. Maybe after the private fields >> >> >> >> problem >> >> >> >> has >> >> >> >> been resolved, someone will figure out a better way to handle >> >> >> >> your >> >> >> >> use >> >> >> >> cases. >> >> >> >> >> >> >> >> >> >> >> >> On Sat, Jul 28, 2018 at 3:52 PM Darien Valentine >> >> >> >> <valentinium at gmail.com> >> >> >> >> wrote: >> >> >> >>> >> >> >> >>> > Are you saying you want multiple non-hierarchally related >> >> >> >>> > classes >> >> >> >>> > to >> >> >> >>> > have an instance private field with shared name [...] >> >> >> >>> >> >> >> >>> Yeah. This is a hard problem to solve when trying to integrate >> >> >> >>> private >> >> >> >>> fields with class syntax, but it’s not a problem at all when >> >> >> >>> privacy >> >> >> >>> is a >> >> >> >>> more generic tool based on scope. This also isn’t a foreign >> >> >> >>> concept >> >> >> >>> in >> >> >> >>> ES: >> >> >> >>> consider this intrinsic method: >> >> >> >>> >> >> >> >>> https://tc39.github.io/ecma262/#sec-arraybuffer.isview >> >> >> >>> >> >> >> >>> This method returns true if the argument has the >> >> >> >>> `[[ViewedArrayBuffer]]` >> >> >> >>> slot. This slot exists on genuine instances of both >> >> >> >>> `%TypedArray%` >> >> >> >>> and >> >> >> >>> `%DataView%`, but they do not receive these slots by way of >> >> >> >>> inheritance from >> >> >> >>> a common constructor. There are similar cases in HTML host APIs. >> >> >> >>> >> >> >> >>> > The befriend keyword would allow an object to request >> >> >> >>> > friendship >> >> >> >>> > with >> >> >> >>> > an existing friendly object. I'm not sure this is a good idea, >> >> >> >>> > though. >> >> >> >>> >> >> >> >>> I don’t think it is either, no. It’s too much complexity for too >> >> >> >>> little >> >> >> >>> gain. But again, this is achievable “for free” just by divorcing >> >> >> >>> “private >> >> >> >>> object state” from class declarations (or object literals). I >> >> >> >>> would >> >> >> >>> ask: >> >> >> >>> what problem is solved by making this a feature of the >> >> >> >>> declarations >> >> >> >>> themselves? Does it merit the complexity and the hoop jumping >> >> >> >>> needed >> >> >> >>> to >> >> >> >>> handle edge cases?\* >> >> >> >>> >> >> >> >>> \* One person’s edge case; another’s everyday concern haha. >> >> >> >>> >> >> >> >>> > The example you gave above still declares the functions in >> >> >> >>> > question >> >> >> >>> > inside the class body, so that's not really a solution. >> >> >> >>> >> >> >> >>> If you’re referring to the first example, that is a >> >> >> >>> demonstration >> >> >> >>> of >> >> >> >>> what >> >> >> >>> is possible using the existing stage 3 class fields proposal as >> >> >> >>> implemented >> >> >> >>> in Chrome. It isn’t what I want; it’s what’s necessary to >> >> >> >>> achieve >> >> >> >>> this >> >> >> >>> with >> >> >> >>> the current stage 3 proposed model. >> >> >> >>> >> >> >> >>> > Sounds to me like you'd love for class syntax to look like >> >> >> >>> > this >> >> >> >>> > [[example with mixin syntax in declaration]] >> >> >> >>> >> >> >> >>> Perhaps — it’s interesting for sure! But the pattern that >> >> >> >>> already >> >> >> >>> works, >> >> >> >>> `mixin(Cstr)`, is not presently a source of problems for me. >> >> >> >>> Private >> >> >> >>> object >> >> >> >>> state in particular is only _made complex_ by associating it >> >> >> >>> with >> >> >> >>> declarations instead of scopes that happen to contain >> >> >> >>> declarations >> >> >> >>> (or >> >> >> >>> into >> >> >> >>> which constructors are passed, etc). The complexity is >> >> >> >>> artificial — >> >> >> >>> not a >> >> >> >>> good sign imo. >> >> >> >>> >> >> >> >>> > One thing both proposal-class-fields and >> >> >> >>> > proposal-object-members >> >> >> >>> > have >> >> >> >>> > in common is that the focus is on producing instance-private >> >> >> >>> > fields. >> >> >> >>> > All 3 >> >> >> >>> > of the scenarios you presented lay outside of that focus for >> >> >> >>> > one >> >> >> >>> > reason or >> >> >> >>> > another. >> >> >> >>> >> >> >> >>> Both the WeakMap solution and the stub concept I provided after >> >> >> >>> are >> >> >> >>> more >> >> >> >>> generic than privacy in either of those proposals. When I say >> >> >> >>> "object >> >> >> >>> private state," it’s true that the object in question could be >> >> >> >>> any >> >> >> >>> object. >> >> >> >>> But in practice, any realization of the feature would pertain >> >> >> >>> chiefly >> >> >> >>> to >> >> >> >>> class instances, and the examples I gave, though contrived, do >> >> >> >>> concern >> >> >> >>> class >> >> >> >>> instances. The reason private object state is chiefly an issue >> >> >> >>> of >> >> >> >>> class >> >> >> >>> instances stems directly from the nature of prototype methods >> >> >> >>> and >> >> >> >>> accessors, >> >> >> >>> so if you are not making use of prototypes, you could instead >> >> >> >>> have >> >> >> >>> used a >> >> >> >>> closure+factory directly. >> >> >> >>> >> >> >> >>> --- >> >> >> >>> >> >> >> >>> In a nutshell, my issue with existing proposals could probably >> >> >> >>> be >> >> >> >>> summarized as a concern that they are neither as generic nor as >> >> >> >>> simple >> >> >> >>> as >> >> >> >>> native slots. To be clear, proper “slots” are an internal >> >> >> >>> concept, >> >> >> >>> only >> >> >> >>> observable indirectly — but they are the special sauce >> >> >> >>> underlying a >> >> >> >>> number >> >> >> >>> of behaviors which are presently awkward to achieve in ES code >> >> >> >>> itself, >> >> >> >>> and >> >> >> >>> they are a nice simple model of private object state which is >> >> >> >>> tantalizingly >> >> >> >>> close to, but not _exactly_ the same as in two critical ways, >> >> >> >>> symbol >> >> >> >>> keyed >> >> >> >>> properties. That said, “real” slots would continue to have an >> >> >> >>> advantage with >> >> >> >>> regard to cross-realm stuff even if private symbol keys existed. >> >> >> >>> >> >> >> >>> That such a model is radically simpler — minmax and all that — >> >> >> >>> feels >> >> >> >>> very >> >> >> >>> important to me, but I dunno. I’m not holding my breath for big >> >> >> >>> changes >> >> >> >>> here. The current stage 3 proposal seems to be unstoppable; much >> >> >> >>> smarter / >> >> >> >>> more important people than me have already tried and failed. :) >> >> >> >>> >> >> >> >>> >> >> >> >>> On Sat, Jul 28, 2018 at 3:14 PM Ranando King <kingmph at gmail.com> >> >> >> >>> wrote: >> >> >> >>>> >> >> >> >>>> In a word... wow. You've got me thinking hard here. Those are >> >> >> >>>> some >> >> >> >>>> peculiar use cases, and they do a great job of highlighting why >> >> >> >>>> someone >> >> >> >>>> might forego using `class`. One thing both >> >> >> >>>> proposal-class-fields >> >> >> >>>> and >> >> >> >>>> proposal-object-members have in common is that the focus is on >> >> >> >>>> producing >> >> >> >>>> instance-private fields. All 3 of the scenarios you presented >> >> >> >>>> lay >> >> >> >>>> outside of >> >> >> >>>> that focus for one reason or another. >> >> >> >>>> >> >> >> >>>> > Adding the same “slot” to multiple classes which don’t >> >> >> >>>> > inherit >> >> >> >>>> > from >> >> >> >>>> > each other >> >> >> >>>> >> >> >> >>>> I'm a little confused by this one. Are you saying you want >> >> >> >>>> multiple >> >> >> >>>> non-hierarchally related classes to have an instance private >> >> >> >>>> field >> >> >> >>>> with >> >> >> >>>> shared name, such that the same private field name refers to a >> >> >> >>>> distinct and >> >> >> >>>> separate field on each instance of every such class, but where >> >> >> >>>> any >> >> >> >>>> such >> >> >> >>>> instance can have that field referenced by that shared name >> >> >> >>>> from >> >> >> >>>> any >> >> >> >>>> member >> >> >> >>>> function of the corresponding classes? (Wow that was wordy to >> >> >> >>>> write >> >> >> >>>> out...) >> >> >> >>>> If this is what you meant, you're describing friend classes. >> >> >> >>>> The >> >> >> >>>> top-down >> >> >> >>>> processing nature of ES makes this a difficult thing to create >> >> >> >>>> a >> >> >> >>>> clean >> >> >> >>>> syntax for without risking leaking the private state or >> >> >> >>>> fundamentally >> >> >> >>>> altering how ES is processed. Mutual friendship is even harder. >> >> >> >>>> >> >> >> >>>> ... and yet I just thought of a way to do it. By telling you >> >> >> >>>> this >> >> >> >>>> I'm >> >> >> >>>> leaving myself to consider writing a proposal containing 2 new >> >> >> >>>> keywords: >> >> >> >>>> `befriend` and `friendly`. I don't know if this can be done >> >> >> >>>> with >> >> >> >>>> the >> >> >> >>>> existing proposal being what it is. However, with my proposal, >> >> >> >>>> there's a >> >> >> >>>> chance. The `friendly` keyword would declare that an object is >> >> >> >>>> prepared to >> >> >> >>>> share select information with any object that befriends it. The >> >> >> >>>> `befriend` >> >> >> >>>> keyword would allow an object to request friendship with an >> >> >> >>>> existing >> >> >> >>>> friendly object. I'm not sure this is a good idea, though. This >> >> >> >>>> means >> >> >> >>>> that >> >> >> >>>> any object declared 'friendly' is automatically insecure as all >> >> >> >>>> it >> >> >> >>>> takes to >> >> >> >>>> gain access to the selected members of its private space would >> >> >> >>>> be >> >> >> >>>> to >> >> >> >>>> 'befriend' it. >> >> >> >>>> >> >> >> >>>> > Selectively sharing access to private state through functions >> >> >> >>>> > declared >> >> >> >>>> > outside the class body >> >> >> >>>> >> >> >> >>>> The example you gave above still declares the functions in >> >> >> >>>> question >> >> >> >>>> inside the `class` body, so that's not really a solution. If >> >> >> >>>> the >> >> >> >>>> example you >> >> >> >>>> gave actually solves your use case, then what you're asking for >> >> >> >>>> here >> >> >> >>>> isn't >> >> >> >>>> even needed. If, however, that was a bad example, then it >> >> >> >>>> sounds >> >> >> >>>> like >> >> >> >>>> you're >> >> >> >>>> looking for friend functions. See the previous section. >> >> >> >>>> >> >> >> >>>> > Adding slots dynamically, e.g. when adding mix-in methods >> >> >> >>>> > that >> >> >> >>>> > may >> >> >> >>>> > initialize a new slot if necessary when called, since >> >> >> >>>> > subclassing >> >> >> >>>> > is not >> >> >> >>>> > always appropriate >> >> >> >>>> >> >> >> >>>> Sounds to me like you'd love for `class` syntax to look like >> >> >> >>>> this: >> >> >> >>>> >> >> >> >>>> ```js >> >> >> >>>> class [<identifierName1>] [extends <identifierName2>] [mixes >> >> >> >>>> <identifierName3>[, <identifierName3>[, ...]]] { ... } >> >> >> >>>> ``` >> >> >> >>>> so that the private fields of the objects in the `mixes` list >> >> >> >>>> are >> >> >> >>>> added >> >> >> >>>> to the set of private fields provided by the `class` definition >> >> >> >>>> directly. >> >> >> >>>> That would also require another proposal, but I think that can >> >> >> >>>> be >> >> >> >>>> done >> >> >> >>>> regardless of which instance-private fields proposal gets >> >> >> >>>> accepted. >> >> >> >>>> >> >> >> >>>> On Sat, Jul 28, 2018 at 12:49 PM Darien Valentine >> >> >> >>>> <valentinium at gmail.com> wrote: >> >> >> >>>>> >> >> >> >>>>> To put this another, much briefer way, here’s a hypothetical >> >> >> >>>>> model >> >> >> >>>>> for >> >> >> >>>>> associating private state with objects that would cover me. >> >> >> >>>>> Privacy >> >> >> >>>>> would be >> >> >> >>>>> provided... >> >> >> >>>>> >> >> >> >>>>> 1. in the form of symbolic keys whose presence cannot be >> >> >> >>>>> observed >> >> >> >>>>> (i.e., they would not be exposed by `getOwnPropertySymbols`) >> >> >> >>>>> 2. and which have a syntactic declaration so that one can be >> >> >> >>>>> sure >> >> >> >>>>> they >> >> >> >>>>> are really getting private keys (i.e., an api like >> >> >> >>>>> `Symbol.private()` >> >> >> >>>>> wouldn’t work) >> >> >> >>>>> >> >> >> >>>>> ``` >> >> >> >>>>> const bar = private(); >> >> >> >>>>> >> >> >> >>>>> // alternatively: const #bar; could be anything so long as >> >> >> >>>>> it’s >> >> >> >>>>> syntactic >> >> >> >>>>> >> >> >> >>>>> class Foo { >> >> >> >>>>> constructor() { >> >> >> >>>>> this[bar] = 1; >> >> >> >>>>> } >> >> >> >>>>> } >> >> >> >>>>> >> >> >> >>>>> // etc >> >> >> >>>>> ``` >> >> >> >>>>> >> >> >> >>>>> The keys would be typeof 'symbol'; the only difference being >> >> >> >>>>> that >> >> >> >>>>> they >> >> >> >>>>> are symbols which are flagged as private when created. They >> >> >> >>>>> would >> >> >> >>>>> be >> >> >> >>>>> permitted only in syntactic property assignments and accesses. >> >> >> >>>>> Existing >> >> >> >>>>> reflection utilities would disallow the use or appearance of >> >> >> >>>>> such >> >> >> >>>>> symbols >> >> >> >>>>> both to ensure privacy and to maintain the invariant that they >> >> >> >>>>> are >> >> >> >>>>> always >> >> >> >>>>> simple data properties: >> >> >> >>>>> >> >> >> >>>>> ```js >> >> >> >>>>> Reflect.defineProperty({}, #bar, { ... }); // throws type >> >> >> >>>>> error >> >> >> >>>>> Object.getOwnPropertyDescriptors(someObjWithAPrivateSlot); // >> >> >> >>>>> does >> >> >> >>>>> not >> >> >> >>>>> include it >> >> >> >>>>> foo[bar] = 2; // fine >> >> >> >>>>> ``` >> >> >> >>>>> >> >> >> >>>>> This is significantly simpler than what’s in flight both in >> >> >> >>>>> terms >> >> >> >>>>> of >> >> >> >>>>> syntax and mechanics, which makes me suspicious that I’m >> >> >> >>>>> probably >> >> >> >>>>> ignoring >> >> >> >>>>> things that other people find important. However it would >> >> >> >>>>> bring >> >> >> >>>>> parity to ES >> >> >> >>>>> objects wrt being able to implement genuinely private slots in >> >> >> >>>>> userland with >> >> >> >>>>> the same flexibility as what is done internally. >> >> >> >>>>> >> >> >> >>>>> In total, this entails a new primary expression, a boolean >> >> >> >>>>> flag >> >> >> >>>>> associated with symbol values, and an extra step added to >> >> >> >>>>> several >> >> >> >>>>> algorithms >> >> >> >>>>> associated with Object and Reflect. >> >> >> >>>>> _______________________________________________ >> >> >> >>>>> 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
I'll just say it feels inconsistent with how every other property is configured. That the key itself holds magic behavior-changing information. It's not a use case or overhead concern.
I'll just say it feels inconsistent with how every other property is configured. That the key itself holds magic behavior-changing information. It's not a use case or overhead concern. On Monday, July 30, 2018, Isiah Meadows <isiahmeadows at gmail.com> wrote: > Um, no. The use case is *extremely* limited, and that ruins a few > optimizations you could otherwise make with private symbols (like > caching proxy forwarding without having to bail out). > > Besides, whether a symbol is private requires exactly one bit to > store, so there's no real overhead with storing it on the object. > Heck, if you want to optimize it better, you might choose to store > that same bit on both the symbol and the object descriptor itself, and > I'd expect engines to do just that - it saves a pointer dereference. > ----- > > Isiah Meadows > me at isiahmeadows.com > www.isiahmeadows.com > > > On Mon, Jul 30, 2018 at 1:25 AM, Michael Theriot > <michael.lee.theriot at gmail.com> wrote: > > Also throwing this out there, symbols would now carry additional > > information: private or normal. Would it be better to configure this on > > objects instead? > > > > E.g. `Object.setPropertySymbolVisibility(object, symbol, true / false)` > > > > (and then ideally sugar for this) > > > > That way a symbol's visibility on an object is information held on the > > object rather than the primitive. A little more work involved, but lines > up > > with Object.defineProperty and symbols remain purely unique identifiers. > > > > On Monday, July 30, 2018, Isiah Meadows <isiahmeadows at gmail.com> wrote: > >> > >> I'm aware it's possible to misuse, but if concerns of misuse were a > >> serious issue, we wouldn't have iterators, for example [1] [2]. But > >> IMHO freeing weak maps from a role they weren't designed for > >> substantially outweighs the risks of abusing them further (and the > >> abuses are incredibly frequent). > >> > >> [1]: > >> https://esdiscuss.org/topic/iterators-generators-finally- > and-scarce-resources-was-april-10-2014-meeting-notes > >> [2]: https://esdiscuss.org/topic/resource-management > >> > >> ----- > >> > >> Isiah Meadows > >> me at isiahmeadows.com > >> www.isiahmeadows.com > >> > >> > >> On Sun, Jul 29, 2018 at 10:55 PM, Michael Theriot > >> <michael.lee.theriot at gmail.com> wrote: > >> > Right, I wouldn't, but I'm concerned others would misuse it. I don't > >> > think > >> > it's a blocker though, and actually frees weakmaps from trying to fill > >> > this > >> > role. > >> > > >> > > >> > On Sunday, July 29, 2018, Isiah Meadows <isiahmeadows at gmail.com> > wrote: > >> >> > >> >> It will, but weak maps will still remain useful for cases when you're > >> >> semantically dealing with a key/value map. In theory, you could > >> >> implement a weak map on top of this [1], but in practice, it doesn't > >> >> always make sense to do it. A good example of this is if you are > >> >> "tagging" an object with data. If this data isn't really part of the > >> >> object itself, you shouldn't be using a private symbol for it. > Another > >> >> good example is if you're doing simple caching and you need to clear > >> >> the weak map by replacing it. Using private symbols for this doesn't > >> >> really fit with the domain here, so you're more likely just to > confuse > >> >> future readers (including yourself) if you do this. > >> >> > >> >> [1]: > >> >> https://gist.github.com/isiahmeadows/a8494868c4b193dfbf7139589f472a > d8 > >> >> ----- > >> >> > >> >> Isiah Meadows > >> >> me at isiahmeadows.com > >> >> www.isiahmeadows.com > >> >> > >> >> > >> >> On Sun, Jul 29, 2018 at 10:05 PM, Michael Theriot > >> >> <michael.lee.theriot at gmail.com> wrote: > >> >> > Private symbols sounds like an easy win. They would be painfully > >> >> > simple, > >> >> > real properties, not just variables with property imitation syntax > >> >> > that > >> >> > undoubtedly confuses people. With the added benefit that children > can > >> >> > truly > >> >> > override the base class, freedom to define private members shared > >> >> > across > >> >> > otherwise unrelated objects, and even injection. My only concern is > >> >> > that > >> >> > it > >> >> > could cross into WeakMap use cases. > >> >> > > >> >> > > >> >> > On Sunday, July 29, 2018, Isiah Meadows <isiahmeadows at gmail.com> > >> >> > wrote: > >> >> >> > >> >> >> BTW, I came up with an alternate proposal for privacy altogether: > >> >> >> https://github.com/tc39/proposal-class-fields/issues/115 > >> >> >> > >> >> >> TL;DR: private symbols that proxies can't see and that can't be > >> >> >> enumerated. > >> >> >> ----- > >> >> >> > >> >> >> Isiah Meadows > >> >> >> me at isiahmeadows.com > >> >> >> www.isiahmeadows.com > >> >> >> > >> >> >> > >> >> >> On Sun, Jul 29, 2018 at 12:23 AM, Darien Valentine > >> >> >> <valentinium at gmail.com> wrote: > >> >> >> >> What you're essentially asking for is a violatable private > field, > >> >> >> >> or > >> >> >> >> as > >> >> >> >> has been described by others, a "soft private". > >> >> >> > > >> >> >> > We might have different definitions here, but I would describe > >> >> >> > what > >> >> >> > I’m > >> >> >> > talking about as hard private. Soft private, at least as it > >> >> >> > appears > >> >> >> > to > >> >> >> > have > >> >> >> > been defined in [prior > >> >> >> > > >> >> >> > > >> >> >> > discussions](https://github.com/tc39/proposal-private- > fields/issues/33), > >> >> >> > described an avenue where symbol keyed properties were given a > new > >> >> >> > syntactic > >> >> >> > form — but they were still just regular symbol keys, and > therefore > >> >> >> > could > >> >> >> > be > >> >> >> > introspected by outside agents who had not been given express > >> >> >> > privilege > >> >> >> > to > >> >> >> > do so: > >> >> >> > > >> >> >> >> [...] the core would be that "private state" is simply (public) > >> >> >> >> symbol-named properties, with syntactic sugar for those > symbols, > >> >> >> >> and > >> >> >> >> possibly some kind of introspection over them [...] > >> >> >> > > >> >> >> > The thread goes on to contrast the soft model with an earlier > >> >> >> > version > >> >> >> > of > >> >> >> > the > >> >> >> > private fields proposal seen today. The hard private example > uses > >> >> >> > the > >> >> >> > class > >> >> >> > declaration as a pseudo-scope, but contrasting these two options > >> >> >> > as > >> >> >> > if > >> >> >> > they > >> >> >> > are binary is not accurate: hard private through > >> >> >> > module/function/block > >> >> >> > scope > >> >> >> > already exists, it is just difficult to work with in the context > >> >> >> > of > >> >> >> > shared > >> >> >> > prototypes — one must either use WeakMaps, technically giving > >> >> >> > _hardness_ > >> >> >> > because of the forgeability of `global.WeakMap` / > >> >> >> > `WeakMap.prototype` > >> >> >> > / > >> >> >> > `WeakMap.prototype.get|has|set`, or be willing to either not > worry > >> >> >> > about > >> >> >> > garbage collection or implement it manually. This could be > solved > >> >> >> > for > >> >> >> > with a > >> >> >> > few rather undramatic changes, though. > >> >> >> > > >> >> >> > Notably, the first post there lists the following as a > >> >> >> > disadvantage > >> >> >> > of > >> >> >> > the > >> >> >> > soft model it describes: > >> >> >> > > >> >> >> >> Platform objects, both within ECMAScript and in embedding > >> >> >> >> environments, > >> >> >> >> contain hard private state. If a library wants to be > >> >> >> >> high-fidelity > >> >> >> >> and > >> >> >> >> just > >> >> >> >> like a platform object, soft-private state does not provide > this > >> >> >> >> (@domenic) > >> >> >> > > >> >> >> > ...but neither model there quite covers that use case. Platform > >> >> >> > objects > >> >> >> > _can_ see each other’s private state (cf the `isView` example > >> >> >> > earlier, > >> >> >> > or > >> >> >> > scan the DOM API specs / Chrome source a bit to find numerous > >> >> >> > examples). > >> >> >> > It’s only the ES layer interacting with their interfaces that > >> >> >> > cannot. > >> >> >> > > >> >> >> > Such things can be achieved with ordinary scope, which is why > the > >> >> >> > WeakMap > >> >> >> > pattern has worked in practice in my experience to date, while > >> >> >> > class-declaration-scoped privacy has not. It isn’t uncommon for > a > >> >> >> > library’s > >> >> >> > exposed interface to be composed of an object graph, where > privacy > >> >> >> > is > >> >> >> > a > >> >> >> > concern at this public interface level, but library internal > state > >> >> >> > may > >> >> >> > be > >> >> >> > interconnected in unexposed ways under the hood. The most > familiar > >> >> >> > example > >> >> >> > of this is a DOM node tree. As an experiment, perhaps try to > >> >> >> > implement > >> >> >> > the > >> >> >> > relationships between HTMLFormElement, > HTMLFormControlsCollection > >> >> >> > and > >> >> >> > the > >> >> >> > various form control elements using either the main private > fields > >> >> >> > proposal > >> >> >> > or your alternative proposal and see what happens. > >> >> >> > > >> >> >> >> However, the guardian logic tries to verify that the function > >> >> >> >> trying > >> >> >> >> to > >> >> >> >> access the private fields of an instance is a member of the > same > >> >> >> >> or > >> >> >> >> descending prototype that was used to create that instance. > >> >> >> > > >> >> >> > Because I’m looking at this in terms of slots, I’d first point > out > >> >> >> > that > >> >> >> > prototypes don’t determine slottedness, the execution of some > >> >> >> > specific > >> >> >> > constructor does. It’s during this process that slots are > >> >> >> > associated > >> >> >> > with > >> >> >> > the newly minted object by its identity. But even the current > >> >> >> > private > >> >> >> > fields > >> >> >> > proposal tracks this behavior closely, and I’m not sure how else > >> >> >> > it > >> >> >> > could > >> >> >> > work. The [[Prototype]] slot of an object is typically mutable > >> >> >> > (`R|O.setPrototypeOf`, `__proto__`) and forgeable (Proxy’s > >> >> >> > `getPrototypeOf` > >> >> >> > trap). Why/how would its value matter when it comes to accessing > >> >> >> > private > >> >> >> > state? > >> >> >> > > >> >> >> > ```js > >> >> >> > const pattern = /foo/; > >> >> >> > Reflect.setPrototypeOf(pattern, Date.prototype); > >> >> >> > pattern instanceof Date; // true > >> >> >> > pattern instanceof RegExp; // false > >> >> >> > pattern.getMinutes(); // throws TypeError because [[DateValue]] > >> >> >> > slot > >> >> >> > is > >> >> >> > missing > >> >> >> > RegExp.prototype.exec.call(pattern, 'foo'); // works; object > has > >> >> >> > RegExp > >> >> >> > private slots > >> >> >> > ``` > >> >> >> > > >> >> >> >> If I removed that requirement, it would work. However, there'd > be > >> >> >> >> no > >> >> >> >> way > >> >> >> >> to keep the private data from being leaked. Sadly, it's all or > >> >> >> >> nothing > >> >> >> >> with > >> >> >> >> this approach. Hard private or soft private, those are the only > >> >> >> >> choices. > >> >> >> > > >> >> >> > In the context of what you’ve described here this may be true, > but > >> >> >> > no > >> >> >> > such > >> >> >> > limitation presently exists. We can already do all this — hard, > >> >> >> > leak-free > >> >> >> > privacy, brandedness, “friends” etc — with scopes and WeakMaps, > >> >> >> > but > >> >> >> > for > >> >> >> > the > >> >> >> > fact that the `WeakMap` intrinsics may be forged. So what’s > >> >> >> > baffled > >> >> >> > me > >> >> >> > is > >> >> >> > this: why are all the proposals exploring this space not > >> >> >> > addressing > >> >> >> > that > >> >> >> > relatively simple existing problem, and instead starting off > from > >> >> >> > a > >> >> >> > place of > >> >> >> > significant new complexity? You said “maybe after the private > >> >> >> > fields > >> >> >> > problem > >> >> >> > has been resolved, someone will figure out a better way to > handle > >> >> >> > your > >> >> >> > use > >> >> >> > cases,” but I’d have hoped for the opposite — I want the > primitive > >> >> >> > building > >> >> >> > blocks which things like class field syntax could be built over, > >> >> >> > if > >> >> >> > it > >> >> >> > is > >> >> >> > found that they are still necessary once the root issue is > solved > >> >> >> > for. > >> >> >> > > >> >> >> >> The main reason the privacy is set on a declaration level is > >> >> >> >> because > >> >> >> >> scope-level inheritance isn't very good for class-oriented > >> >> >> >> inheritance. > >> >> >> > > >> >> >> > Can you explain this more? I’m not sure what’s meant by > >> >> >> > “scope-level > >> >> >> > inheritance” here. > >> >> >> > > >> >> >> >> I don't intend to stop [...] > >> >> >> > > >> >> >> > I very much admire your dedication! I’m also digging the > >> >> >> > discussion. > >> >> >> > I > >> >> >> > think > >> >> >> > we may be representing viewpoints at opposite extremes here, so > >> >> >> > it’s > >> >> >> > an > >> >> >> > interesting contrast, but it also probably means we may be > lacking > >> >> >> > some > >> >> >> > context for understanding one another’s angles. I’d be curious > to > >> >> >> > hear > >> >> >> > more > >> >> >> > about what you see as the problems with the current fields > >> >> >> > proposal + > >> >> >> > how > >> >> >> > your members proposal would solve them; the repo readme didn’t > >> >> >> > seem > >> >> >> > to > >> >> >> > include a rationale section. > >> >> >> > > >> >> >> > On Sat, Jul 28, 2018 at 10:30 PM Ranando King < > kingmph at gmail.com> > >> >> >> > wrote: > >> >> >> >> > >> >> >> >> I've almost given up on making any significant headway in > either > >> >> >> >> adjusting > >> >> >> >> or flat-out correcting the flaws in that proposal, but I don't > >> >> >> >> intend > >> >> >> >> to > >> >> >> >> stop trying until either we get stuck with that proposal, or > they > >> >> >> >> understand > >> >> >> >> and accept what I'm telling them, or logically prove that my > >> >> >> >> concerns > >> >> >> >> are > >> >> >> >> either irrational or inconsequential. > >> >> >> >> > >> >> >> >> > Private object state in particular is only _made complex_ by > >> >> >> >> > associating > >> >> >> >> > it with declarations instead of scopes that happen to contain > >> >> >> >> > declarations > >> >> >> >> > (or into which constructors are passed, etc). The complexity > is > >> >> >> >> > artificial — > >> >> >> >> > not a good sign imo. > >> >> >> >> > >> >> >> >> That's not quite right. What you're essentially asking for is a > >> >> >> >> violatable > >> >> >> >> private field, or as has been described by others, a "soft > >> >> >> >> private". > >> >> >> >> Since > >> >> >> >> we agree that the "friendly" & "befriend" pair is a somewhat > (if > >> >> >> >> not > >> >> >> >> completely) bad idea, I'm going to take 1 more pass at your 3 > >> >> >> >> requests > >> >> >> >> with > >> >> >> >> a different angle. > >> >> >> >> > >> >> >> >> > Adding the same “slot” to multiple classes which don’t > inherit > >> >> >> >> > from > >> >> >> >> > each > >> >> >> >> > other > >> >> >> >> > Selectively sharing access to private state through functions > >> >> >> >> > declared > >> >> >> >> > outside the class body > >> >> >> >> > >> >> >> >> ```js > >> >> >> >> //Using my proposal > >> >> >> >> var {A, B, C} = (() => { > >> >> >> >> const common = Symbol("common"); > >> >> >> >> > >> >> >> >> class A { > >> >> >> >> private [common] = 1; > >> >> >> >> add(...args) { > >> >> >> >> var retval = this#[common]; > >> >> >> >> for (let obj of args) { > >> >> >> >> retval += obj#[common]; > >> >> >> >> } > >> >> >> >> return retval; > >> >> >> >> } > >> >> >> >> } > >> >> >> >> class B { > >> >> >> >> private [common] = 2; > >> >> >> >> optional() { > >> >> >> >> console.log(`common member = ${this#[common]}`); > >> >> >> >> } > >> >> >> >> } > >> >> >> >> var C = { > >> >> >> >> private [common]: 3, > >> >> >> >> required() { > >> >> >> >> console.log(`common member = ${this#[common]}`); > >> >> >> >> } > >> >> >> >> } > >> >> >> >> > >> >> >> >> return { A, B, C }; > >> >> >> >> })(); > >> >> >> >> > >> >> >> >> //So you want the following statement to not throw a TypeError > >> >> >> >> and > >> >> >> >> return > >> >> >> >> 6 > >> >> >> >> (new A()).add(new B(), C); > >> >> >> >> ``` > >> >> >> >> I'm not sure I can make this work in my proposal, and I'm > >> >> >> >> absolutely > >> >> >> >> sure > >> >> >> >> you'd be flatly refused by the other proposal. If a `Symbol` is > >> >> >> >> provided as > >> >> >> >> the `[[IdentifierName]]` of a private or protected field, then > I > >> >> >> >> can > >> >> >> >> let > >> >> >> >> that `Symbol` be both the key and value that are added to the > >> >> >> >> `[[DeclarationInfo]]` and `[[InheritanceInfo]]` records. That > way > >> >> >> >> there > >> >> >> >> will > >> >> >> >> be a common private field name usable by all 3 objects. > However, > >> >> >> >> the > >> >> >> >> guardian logic tries to verify that the function trying to > access > >> >> >> >> the > >> >> >> >> private fields of an instance is a member of the same or > >> >> >> >> descending > >> >> >> >> prototype that was used to create that instance. If I removed > >> >> >> >> that > >> >> >> >> requirement, it would work. However, there'd be no way to keep > >> >> >> >> the > >> >> >> >> private > >> >> >> >> data from being leaked. Sadly, it's all or nothing with this > >> >> >> >> approach. > >> >> >> >> Hard > >> >> >> >> private or soft private, those are the only choices. The TC39 > >> >> >> >> board > >> >> >> >> has > >> >> >> >> already decided that what they want new syntax for is hard > >> >> >> >> private. > >> >> >> >> > >> >> >> >> > Adding slots dynamically, e.g. when adding mix-in methods > that > >> >> >> >> > may > >> >> >> >> > initialize a new slot if necessary when called, since > >> >> >> >> > subclassing > >> >> >> >> > is > >> >> >> >> > not > >> >> >> >> > always appropriate > >> >> >> >> > >> >> >> >> Because the TC39 board has set their sights on hard private, > this > >> >> >> >> will > >> >> >> >> require new syntax like what I suggested earlier Adding private > >> >> >> >> members > >> >> >> >> dynamically would also pose a leak risk if it could be done > after > >> >> >> >> the > >> >> >> >> prototype has been fully constructed. The main reason the > privacy > >> >> >> >> is > >> >> >> >> set on > >> >> >> >> a declaration level is because scope-level inheritance isn't > very > >> >> >> >> good > >> >> >> >> for > >> >> >> >> `class`-oriented inheritance. The `class` keyword was provided > to > >> >> >> >> simplify > >> >> >> >> the vertical inheritance model, along with some API to enable > >> >> >> >> inheritance > >> >> >> >> from native objects even without using `class`. The syntax > >> >> >> >> changes > >> >> >> >> for > >> >> >> >> simplifying private field declaration are just an extension of > >> >> >> >> that. > >> >> >> >> Even > >> >> >> >> though it's not unusual for some developers to spend a lot of > >> >> >> >> time > >> >> >> >> working > >> >> >> >> with fringe use-cases, syntax changes are almost always going > to > >> >> >> >> be > >> >> >> >> made for > >> >> >> >> the most common use cases first. Maybe after the private fields > >> >> >> >> problem > >> >> >> >> has > >> >> >> >> been resolved, someone will figure out a better way to handle > >> >> >> >> your > >> >> >> >> use > >> >> >> >> cases. > >> >> >> >> > >> >> >> >> > >> >> >> >> On Sat, Jul 28, 2018 at 3:52 PM Darien Valentine > >> >> >> >> <valentinium at gmail.com> > >> >> >> >> wrote: > >> >> >> >>> > >> >> >> >>> > Are you saying you want multiple non-hierarchally related > >> >> >> >>> > classes > >> >> >> >>> > to > >> >> >> >>> > have an instance private field with shared name [...] > >> >> >> >>> > >> >> >> >>> Yeah. This is a hard problem to solve when trying to integrate > >> >> >> >>> private > >> >> >> >>> fields with class syntax, but it’s not a problem at all when > >> >> >> >>> privacy > >> >> >> >>> is a > >> >> >> >>> more generic tool based on scope. This also isn’t a foreign > >> >> >> >>> concept > >> >> >> >>> in > >> >> >> >>> ES: > >> >> >> >>> consider this intrinsic method: > >> >> >> >>> > >> >> >> >>> https://tc39.github.io/ecma262/#sec-arraybuffer.isview > >> >> >> >>> > >> >> >> >>> This method returns true if the argument has the > >> >> >> >>> `[[ViewedArrayBuffer]]` > >> >> >> >>> slot. This slot exists on genuine instances of both > >> >> >> >>> `%TypedArray%` > >> >> >> >>> and > >> >> >> >>> `%DataView%`, but they do not receive these slots by way of > >> >> >> >>> inheritance from > >> >> >> >>> a common constructor. There are similar cases in HTML host > APIs. > >> >> >> >>> > >> >> >> >>> > The befriend keyword would allow an object to request > >> >> >> >>> > friendship > >> >> >> >>> > with > >> >> >> >>> > an existing friendly object. I'm not sure this is a good > idea, > >> >> >> >>> > though. > >> >> >> >>> > >> >> >> >>> I don’t think it is either, no. It’s too much complexity for > too > >> >> >> >>> little > >> >> >> >>> gain. But again, this is achievable “for free” just by > divorcing > >> >> >> >>> “private > >> >> >> >>> object state” from class declarations (or object literals). I > >> >> >> >>> would > >> >> >> >>> ask: > >> >> >> >>> what problem is solved by making this a feature of the > >> >> >> >>> declarations > >> >> >> >>> themselves? Does it merit the complexity and the hoop jumping > >> >> >> >>> needed > >> >> >> >>> to > >> >> >> >>> handle edge cases?\* > >> >> >> >>> > >> >> >> >>> \* One person’s edge case; another’s everyday concern haha. > >> >> >> >>> > >> >> >> >>> > The example you gave above still declares the functions in > >> >> >> >>> > question > >> >> >> >>> > inside the class body, so that's not really a solution. > >> >> >> >>> > >> >> >> >>> If you’re referring to the first example, that is a > >> >> >> >>> demonstration > >> >> >> >>> of > >> >> >> >>> what > >> >> >> >>> is possible using the existing stage 3 class fields proposal > as > >> >> >> >>> implemented > >> >> >> >>> in Chrome. It isn’t what I want; it’s what’s necessary to > >> >> >> >>> achieve > >> >> >> >>> this > >> >> >> >>> with > >> >> >> >>> the current stage 3 proposed model. > >> >> >> >>> > >> >> >> >>> > Sounds to me like you'd love for class syntax to look like > >> >> >> >>> > this > >> >> >> >>> > [[example with mixin syntax in declaration]] > >> >> >> >>> > >> >> >> >>> Perhaps — it’s interesting for sure! But the pattern that > >> >> >> >>> already > >> >> >> >>> works, > >> >> >> >>> `mixin(Cstr)`, is not presently a source of problems for me. > >> >> >> >>> Private > >> >> >> >>> object > >> >> >> >>> state in particular is only _made complex_ by associating it > >> >> >> >>> with > >> >> >> >>> declarations instead of scopes that happen to contain > >> >> >> >>> declarations > >> >> >> >>> (or > >> >> >> >>> into > >> >> >> >>> which constructors are passed, etc). The complexity is > >> >> >> >>> artificial — > >> >> >> >>> not a > >> >> >> >>> good sign imo. > >> >> >> >>> > >> >> >> >>> > One thing both proposal-class-fields and > >> >> >> >>> > proposal-object-members > >> >> >> >>> > have > >> >> >> >>> > in common is that the focus is on producing instance-private > >> >> >> >>> > fields. > >> >> >> >>> > All 3 > >> >> >> >>> > of the scenarios you presented lay outside of that focus for > >> >> >> >>> > one > >> >> >> >>> > reason or > >> >> >> >>> > another. > >> >> >> >>> > >> >> >> >>> Both the WeakMap solution and the stub concept I provided > after > >> >> >> >>> are > >> >> >> >>> more > >> >> >> >>> generic than privacy in either of those proposals. When I say > >> >> >> >>> "object > >> >> >> >>> private state," it’s true that the object in question could be > >> >> >> >>> any > >> >> >> >>> object. > >> >> >> >>> But in practice, any realization of the feature would pertain > >> >> >> >>> chiefly > >> >> >> >>> to > >> >> >> >>> class instances, and the examples I gave, though contrived, do > >> >> >> >>> concern > >> >> >> >>> class > >> >> >> >>> instances. The reason private object state is chiefly an issue > >> >> >> >>> of > >> >> >> >>> class > >> >> >> >>> instances stems directly from the nature of prototype methods > >> >> >> >>> and > >> >> >> >>> accessors, > >> >> >> >>> so if you are not making use of prototypes, you could instead > >> >> >> >>> have > >> >> >> >>> used a > >> >> >> >>> closure+factory directly. > >> >> >> >>> > >> >> >> >>> --- > >> >> >> >>> > >> >> >> >>> In a nutshell, my issue with existing proposals could probably > >> >> >> >>> be > >> >> >> >>> summarized as a concern that they are neither as generic nor > as > >> >> >> >>> simple > >> >> >> >>> as > >> >> >> >>> native slots. To be clear, proper “slots” are an internal > >> >> >> >>> concept, > >> >> >> >>> only > >> >> >> >>> observable indirectly — but they are the special sauce > >> >> >> >>> underlying a > >> >> >> >>> number > >> >> >> >>> of behaviors which are presently awkward to achieve in ES code > >> >> >> >>> itself, > >> >> >> >>> and > >> >> >> >>> they are a nice simple model of private object state which is > >> >> >> >>> tantalizingly > >> >> >> >>> close to, but not _exactly_ the same as in two critical ways, > >> >> >> >>> symbol > >> >> >> >>> keyed > >> >> >> >>> properties. That said, “real” slots would continue to have an > >> >> >> >>> advantage with > >> >> >> >>> regard to cross-realm stuff even if private symbol keys > existed. > >> >> >> >>> > >> >> >> >>> That such a model is radically simpler — minmax and all that — > >> >> >> >>> feels > >> >> >> >>> very > >> >> >> >>> important to me, but I dunno. I’m not holding my breath for > big > >> >> >> >>> changes > >> >> >> >>> here. The current stage 3 proposal seems to be unstoppable; > much > >> >> >> >>> smarter / > >> >> >> >>> more important people than me have already tried and failed. > :) > >> >> >> >>> > >> >> >> >>> > >> >> >> >>> On Sat, Jul 28, 2018 at 3:14 PM Ranando King < > kingmph at gmail.com> > >> >> >> >>> wrote: > >> >> >> >>>> > >> >> >> >>>> In a word... wow. You've got me thinking hard here. Those are > >> >> >> >>>> some > >> >> >> >>>> peculiar use cases, and they do a great job of highlighting > why > >> >> >> >>>> someone > >> >> >> >>>> might forego using `class`. One thing both > >> >> >> >>>> proposal-class-fields > >> >> >> >>>> and > >> >> >> >>>> proposal-object-members have in common is that the focus is > on > >> >> >> >>>> producing > >> >> >> >>>> instance-private fields. All 3 of the scenarios you presented > >> >> >> >>>> lay > >> >> >> >>>> outside of > >> >> >> >>>> that focus for one reason or another. > >> >> >> >>>> > >> >> >> >>>> > Adding the same “slot” to multiple classes which don’t > >> >> >> >>>> > inherit > >> >> >> >>>> > from > >> >> >> >>>> > each other > >> >> >> >>>> > >> >> >> >>>> I'm a little confused by this one. Are you saying you want > >> >> >> >>>> multiple > >> >> >> >>>> non-hierarchally related classes to have an instance private > >> >> >> >>>> field > >> >> >> >>>> with > >> >> >> >>>> shared name, such that the same private field name refers to > a > >> >> >> >>>> distinct and > >> >> >> >>>> separate field on each instance of every such class, but > where > >> >> >> >>>> any > >> >> >> >>>> such > >> >> >> >>>> instance can have that field referenced by that shared name > >> >> >> >>>> from > >> >> >> >>>> any > >> >> >> >>>> member > >> >> >> >>>> function of the corresponding classes? (Wow that was wordy to > >> >> >> >>>> write > >> >> >> >>>> out...) > >> >> >> >>>> If this is what you meant, you're describing friend classes. > >> >> >> >>>> The > >> >> >> >>>> top-down > >> >> >> >>>> processing nature of ES makes this a difficult thing to > create > >> >> >> >>>> a > >> >> >> >>>> clean > >> >> >> >>>> syntax for without risking leaking the private state or > >> >> >> >>>> fundamentally > >> >> >> >>>> altering how ES is processed. Mutual friendship is even > harder. > >> >> >> >>>> > >> >> >> >>>> ... and yet I just thought of a way to do it. By telling you > >> >> >> >>>> this > >> >> >> >>>> I'm > >> >> >> >>>> leaving myself to consider writing a proposal containing 2 > new > >> >> >> >>>> keywords: > >> >> >> >>>> `befriend` and `friendly`. I don't know if this can be done > >> >> >> >>>> with > >> >> >> >>>> the > >> >> >> >>>> existing proposal being what it is. However, with my > proposal, > >> >> >> >>>> there's a > >> >> >> >>>> chance. The `friendly` keyword would declare that an object > is > >> >> >> >>>> prepared to > >> >> >> >>>> share select information with any object that befriends it. > The > >> >> >> >>>> `befriend` > >> >> >> >>>> keyword would allow an object to request friendship with an > >> >> >> >>>> existing > >> >> >> >>>> friendly object. I'm not sure this is a good idea, though. > This > >> >> >> >>>> means > >> >> >> >>>> that > >> >> >> >>>> any object declared 'friendly' is automatically insecure as > all > >> >> >> >>>> it > >> >> >> >>>> takes to > >> >> >> >>>> gain access to the selected members of its private space > would > >> >> >> >>>> be > >> >> >> >>>> to > >> >> >> >>>> 'befriend' it. > >> >> >> >>>> > >> >> >> >>>> > Selectively sharing access to private state through > functions > >> >> >> >>>> > declared > >> >> >> >>>> > outside the class body > >> >> >> >>>> > >> >> >> >>>> The example you gave above still declares the functions in > >> >> >> >>>> question > >> >> >> >>>> inside the `class` body, so that's not really a solution. If > >> >> >> >>>> the > >> >> >> >>>> example you > >> >> >> >>>> gave actually solves your use case, then what you're asking > for > >> >> >> >>>> here > >> >> >> >>>> isn't > >> >> >> >>>> even needed. If, however, that was a bad example, then it > >> >> >> >>>> sounds > >> >> >> >>>> like > >> >> >> >>>> you're > >> >> >> >>>> looking for friend functions. See the previous section. > >> >> >> >>>> > >> >> >> >>>> > Adding slots dynamically, e.g. when adding mix-in methods > >> >> >> >>>> > that > >> >> >> >>>> > may > >> >> >> >>>> > initialize a new slot if necessary when called, since > >> >> >> >>>> > subclassing > >> >> >> >>>> > is not > >> >> >> >>>> > always appropriate > >> >> >> >>>> > >> >> >> >>>> Sounds to me like you'd love for `class` syntax to look like > >> >> >> >>>> this: > >> >> >> >>>> > >> >> >> >>>> ```js > >> >> >> >>>> class [<identifierName1>] [extends <identifierName2>] [mixes > >> >> >> >>>> <identifierName3>[, <identifierName3>[, ...]]] { ... } > >> >> >> >>>> ``` > >> >> >> >>>> so that the private fields of the objects in the `mixes` list > >> >> >> >>>> are > >> >> >> >>>> added > >> >> >> >>>> to the set of private fields provided by the `class` > definition > >> >> >> >>>> directly. > >> >> >> >>>> That would also require another proposal, but I think that > can > >> >> >> >>>> be > >> >> >> >>>> done > >> >> >> >>>> regardless of which instance-private fields proposal gets > >> >> >> >>>> accepted. > >> >> >> >>>> > >> >> >> >>>> On Sat, Jul 28, 2018 at 12:49 PM Darien Valentine > >> >> >> >>>> <valentinium at gmail.com> wrote: > >> >> >> >>>>> > >> >> >> >>>>> To put this another, much briefer way, here’s a hypothetical > >> >> >> >>>>> model > >> >> >> >>>>> for > >> >> >> >>>>> associating private state with objects that would cover me. > >> >> >> >>>>> Privacy > >> >> >> >>>>> would be > >> >> >> >>>>> provided... > >> >> >> >>>>> > >> >> >> >>>>> 1. in the form of symbolic keys whose presence cannot be > >> >> >> >>>>> observed > >> >> >> >>>>> (i.e., they would not be exposed by `getOwnPropertySymbols`) > >> >> >> >>>>> 2. and which have a syntactic declaration so that one can be > >> >> >> >>>>> sure > >> >> >> >>>>> they > >> >> >> >>>>> are really getting private keys (i.e., an api like > >> >> >> >>>>> `Symbol.private()` > >> >> >> >>>>> wouldn’t work) > >> >> >> >>>>> > >> >> >> >>>>> ``` > >> >> >> >>>>> const bar = private(); > >> >> >> >>>>> > >> >> >> >>>>> // alternatively: const #bar; could be anything so long as > >> >> >> >>>>> it’s > >> >> >> >>>>> syntactic > >> >> >> >>>>> > >> >> >> >>>>> class Foo { > >> >> >> >>>>> constructor() { > >> >> >> >>>>> this[bar] = 1; > >> >> >> >>>>> } > >> >> >> >>>>> } > >> >> >> >>>>> > >> >> >> >>>>> // etc > >> >> >> >>>>> ``` > >> >> >> >>>>> > >> >> >> >>>>> The keys would be typeof 'symbol'; the only difference being > >> >> >> >>>>> that > >> >> >> >>>>> they > >> >> >> >>>>> are symbols which are flagged as private when created. They > >> >> >> >>>>> would > >> >> >> >>>>> be > >> >> >> >>>>> permitted only in syntactic property assignments and > accesses. > >> >> >> >>>>> Existing > >> >> >> >>>>> reflection utilities would disallow the use or appearance of > >> >> >> >>>>> such > >> >> >> >>>>> symbols > >> >> >> >>>>> both to ensure privacy and to maintain the invariant that > they > >> >> >> >>>>> are > >> >> >> >>>>> always > >> >> >> >>>>> simple data properties: > >> >> >> >>>>> > >> >> >> >>>>> ```js > >> >> >> >>>>> Reflect.defineProperty({}, #bar, { ... }); // throws type > >> >> >> >>>>> error > >> >> >> >>>>> Object.getOwnPropertyDescriptors(someObjWithAPrivateSlot); > // > >> >> >> >>>>> does > >> >> >> >>>>> not > >> >> >> >>>>> include it > >> >> >> >>>>> foo[bar] = 2; // fine > >> >> >> >>>>> ``` > >> >> >> >>>>> > >> >> >> >>>>> This is significantly simpler than what’s in flight both in > >> >> >> >>>>> terms > >> >> >> >>>>> of > >> >> >> >>>>> syntax and mechanics, which makes me suspicious that I’m > >> >> >> >>>>> probably > >> >> >> >>>>> ignoring > >> >> >> >>>>> things that other people find important. However it would > >> >> >> >>>>> bring > >> >> >> >>>>> parity to ES > >> >> >> >>>>> objects wrt being able to implement genuinely private slots > in > >> >> >> >>>>> userland with > >> >> >> >>>>> the same flexibility as what is done internally. > >> >> >> >>>>> > >> >> >> >>>>> In total, this entails a new primary expression, a boolean > >> >> >> >>>>> flag > >> >> >> >>>>> associated with symbol values, and an extra step added to > >> >> >> >>>>> several > >> >> >> >>>>> algorithms > >> >> >> >>>>> associated with Object and Reflect. > >> >> >> >>>>> _______________________________________________ > >> >> >> >>>>> 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/20180730/ad716127/attachment-0001.html>
Isn't this just a different version of the private names idea that Kevin & Daniel were pushing before settling into "proposal-class-fields"? If not, then what's the difference?
Isn't this just a different version of the private names idea that Kevin & Daniel were pushing before settling into "proposal-class-fields"? If not, then what's the difference? On Mon, Jul 30, 2018 at 6:01 AM Michael Theriot < michael.lee.theriot at gmail.com> wrote: > I'll just say it feels inconsistent with how every other property is > configured. That the key itself holds magic behavior-changing information. > It's not a use case or overhead concern. > > On Monday, July 30, 2018, Isiah Meadows <isiahmeadows at gmail.com> wrote: > >> Um, no. The use case is *extremely* limited, and that ruins a few >> optimizations you could otherwise make with private symbols (like >> caching proxy forwarding without having to bail out). >> >> Besides, whether a symbol is private requires exactly one bit to >> store, so there's no real overhead with storing it on the object. >> Heck, if you want to optimize it better, you might choose to store >> that same bit on both the symbol and the object descriptor itself, and >> I'd expect engines to do just that - it saves a pointer dereference. >> ----- >> >> Isiah Meadows >> me at isiahmeadows.com >> www.isiahmeadows.com >> >> >> On Mon, Jul 30, 2018 at 1:25 AM, Michael Theriot >> <michael.lee.theriot at gmail.com> wrote: >> > Also throwing this out there, symbols would now carry additional >> > information: private or normal. Would it be better to configure this on >> > objects instead? >> > >> > E.g. `Object.setPropertySymbolVisibility(object, symbol, true / false)` >> > >> > (and then ideally sugar for this) >> > >> > That way a symbol's visibility on an object is information held on the >> > object rather than the primitive. A little more work involved, but >> lines up >> > with Object.defineProperty and symbols remain purely unique identifiers. >> > >> > On Monday, July 30, 2018, Isiah Meadows <isiahmeadows at gmail.com> wrote: >> >> >> >> I'm aware it's possible to misuse, but if concerns of misuse were a >> >> serious issue, we wouldn't have iterators, for example [1] [2]. But >> >> IMHO freeing weak maps from a role they weren't designed for >> >> substantially outweighs the risks of abusing them further (and the >> >> abuses are incredibly frequent). >> >> >> >> [1]: >> >> >> https://esdiscuss.org/topic/iterators-generators-finally-and-scarce-resources-was-april-10-2014-meeting-notes >> >> [2]: https://esdiscuss.org/topic/resource-management >> >> >> >> ----- >> >> >> >> Isiah Meadows >> >> me at isiahmeadows.com >> >> www.isiahmeadows.com >> >> >> >> >> >> On Sun, Jul 29, 2018 at 10:55 PM, Michael Theriot >> >> <michael.lee.theriot at gmail.com> wrote: >> >> > Right, I wouldn't, but I'm concerned others would misuse it. I don't >> >> > think >> >> > it's a blocker though, and actually frees weakmaps from trying to >> fill >> >> > this >> >> > role. >> >> > >> >> > >> >> > On Sunday, July 29, 2018, Isiah Meadows <isiahmeadows at gmail.com> >> wrote: >> >> >> >> >> >> It will, but weak maps will still remain useful for cases when >> you're >> >> >> semantically dealing with a key/value map. In theory, you could >> >> >> implement a weak map on top of this [1], but in practice, it doesn't >> >> >> always make sense to do it. A good example of this is if you are >> >> >> "tagging" an object with data. If this data isn't really part of the >> >> >> object itself, you shouldn't be using a private symbol for it. >> Another >> >> >> good example is if you're doing simple caching and you need to clear >> >> >> the weak map by replacing it. Using private symbols for this doesn't >> >> >> really fit with the domain here, so you're more likely just to >> confuse >> >> >> future readers (including yourself) if you do this. >> >> >> >> >> >> [1]: >> >> >> >> https://gist.github.com/isiahmeadows/a8494868c4b193dfbf7139589f472ad8 >> >> >> ----- >> >> >> >> >> >> Isiah Meadows >> >> >> me at isiahmeadows.com >> >> >> www.isiahmeadows.com >> >> >> >> >> >> >> >> >> On Sun, Jul 29, 2018 at 10:05 PM, Michael Theriot >> >> >> <michael.lee.theriot at gmail.com> wrote: >> >> >> > Private symbols sounds like an easy win. They would be painfully >> >> >> > simple, >> >> >> > real properties, not just variables with property imitation syntax >> >> >> > that >> >> >> > undoubtedly confuses people. With the added benefit that children >> can >> >> >> > truly >> >> >> > override the base class, freedom to define private members shared >> >> >> > across >> >> >> > otherwise unrelated objects, and even injection. My only concern >> is >> >> >> > that >> >> >> > it >> >> >> > could cross into WeakMap use cases. >> >> >> > >> >> >> > >> >> >> > On Sunday, July 29, 2018, Isiah Meadows <isiahmeadows at gmail.com> >> >> >> > wrote: >> >> >> >> >> >> >> >> BTW, I came up with an alternate proposal for privacy altogether: >> >> >> >> https://github.com/tc39/proposal-class-fields/issues/115 >> >> >> >> >> >> >> >> TL;DR: private symbols that proxies can't see and that can't be >> >> >> >> enumerated. >> >> >> >> ----- >> >> >> >> >> >> >> >> Isiah Meadows >> >> >> >> me at isiahmeadows.com >> >> >> >> www.isiahmeadows.com >> >> >> >> >> >> >> >> >> >> >> >> On Sun, Jul 29, 2018 at 12:23 AM, Darien Valentine >> >> >> >> <valentinium at gmail.com> wrote: >> >> >> >> >> What you're essentially asking for is a violatable private >> field, >> >> >> >> >> or >> >> >> >> >> as >> >> >> >> >> has been described by others, a "soft private". >> >> >> >> > >> >> >> >> > We might have different definitions here, but I would describe >> >> >> >> > what >> >> >> >> > I’m >> >> >> >> > talking about as hard private. Soft private, at least as it >> >> >> >> > appears >> >> >> >> > to >> >> >> >> > have >> >> >> >> > been defined in [prior >> >> >> >> > >> >> >> >> > >> >> >> >> > discussions]( >> https://github.com/tc39/proposal-private-fields/issues/33), >> >> >> >> > described an avenue where symbol keyed properties were given a >> new >> >> >> >> > syntactic >> >> >> >> > form — but they were still just regular symbol keys, and >> therefore >> >> >> >> > could >> >> >> >> > be >> >> >> >> > introspected by outside agents who had not been given express >> >> >> >> > privilege >> >> >> >> > to >> >> >> >> > do so: >> >> >> >> > >> >> >> >> >> [...] the core would be that "private state" is simply >> (public) >> >> >> >> >> symbol-named properties, with syntactic sugar for those >> symbols, >> >> >> >> >> and >> >> >> >> >> possibly some kind of introspection over them [...] >> >> >> >> > >> >> >> >> > The thread goes on to contrast the soft model with an earlier >> >> >> >> > version >> >> >> >> > of >> >> >> >> > the >> >> >> >> > private fields proposal seen today. The hard private example >> uses >> >> >> >> > the >> >> >> >> > class >> >> >> >> > declaration as a pseudo-scope, but contrasting these two >> options >> >> >> >> > as >> >> >> >> > if >> >> >> >> > they >> >> >> >> > are binary is not accurate: hard private through >> >> >> >> > module/function/block >> >> >> >> > scope >> >> >> >> > already exists, it is just difficult to work with in the >> context >> >> >> >> > of >> >> >> >> > shared >> >> >> >> > prototypes — one must either use WeakMaps, technically giving >> >> >> >> > _hardness_ >> >> >> >> > because of the forgeability of `global.WeakMap` / >> >> >> >> > `WeakMap.prototype` >> >> >> >> > / >> >> >> >> > `WeakMap.prototype.get|has|set`, or be willing to either not >> worry >> >> >> >> > about >> >> >> >> > garbage collection or implement it manually. This could be >> solved >> >> >> >> > for >> >> >> >> > with a >> >> >> >> > few rather undramatic changes, though. >> >> >> >> > >> >> >> >> > Notably, the first post there lists the following as a >> >> >> >> > disadvantage >> >> >> >> > of >> >> >> >> > the >> >> >> >> > soft model it describes: >> >> >> >> > >> >> >> >> >> Platform objects, both within ECMAScript and in embedding >> >> >> >> >> environments, >> >> >> >> >> contain hard private state. If a library wants to be >> >> >> >> >> high-fidelity >> >> >> >> >> and >> >> >> >> >> just >> >> >> >> >> like a platform object, soft-private state does not provide >> this >> >> >> >> >> (@domenic) >> >> >> >> > >> >> >> >> > ...but neither model there quite covers that use case. Platform >> >> >> >> > objects >> >> >> >> > _can_ see each other’s private state (cf the `isView` example >> >> >> >> > earlier, >> >> >> >> > or >> >> >> >> > scan the DOM API specs / Chrome source a bit to find numerous >> >> >> >> > examples). >> >> >> >> > It’s only the ES layer interacting with their interfaces that >> >> >> >> > cannot. >> >> >> >> > >> >> >> >> > Such things can be achieved with ordinary scope, which is why >> the >> >> >> >> > WeakMap >> >> >> >> > pattern has worked in practice in my experience to date, while >> >> >> >> > class-declaration-scoped privacy has not. It isn’t uncommon >> for a >> >> >> >> > library’s >> >> >> >> > exposed interface to be composed of an object graph, where >> privacy >> >> >> >> > is >> >> >> >> > a >> >> >> >> > concern at this public interface level, but library internal >> state >> >> >> >> > may >> >> >> >> > be >> >> >> >> > interconnected in unexposed ways under the hood. The most >> familiar >> >> >> >> > example >> >> >> >> > of this is a DOM node tree. As an experiment, perhaps try to >> >> >> >> > implement >> >> >> >> > the >> >> >> >> > relationships between HTMLFormElement, >> HTMLFormControlsCollection >> >> >> >> > and >> >> >> >> > the >> >> >> >> > various form control elements using either the main private >> fields >> >> >> >> > proposal >> >> >> >> > or your alternative proposal and see what happens. >> >> >> >> > >> >> >> >> >> However, the guardian logic tries to verify that the function >> >> >> >> >> trying >> >> >> >> >> to >> >> >> >> >> access the private fields of an instance is a member of the >> same >> >> >> >> >> or >> >> >> >> >> descending prototype that was used to create that instance. >> >> >> >> > >> >> >> >> > Because I’m looking at this in terms of slots, I’d first point >> out >> >> >> >> > that >> >> >> >> > prototypes don’t determine slottedness, the execution of some >> >> >> >> > specific >> >> >> >> > constructor does. It’s during this process that slots are >> >> >> >> > associated >> >> >> >> > with >> >> >> >> > the newly minted object by its identity. But even the current >> >> >> >> > private >> >> >> >> > fields >> >> >> >> > proposal tracks this behavior closely, and I’m not sure how >> else >> >> >> >> > it >> >> >> >> > could >> >> >> >> > work. The [[Prototype]] slot of an object is typically mutable >> >> >> >> > (`R|O.setPrototypeOf`, `__proto__`) and forgeable (Proxy’s >> >> >> >> > `getPrototypeOf` >> >> >> >> > trap). Why/how would its value matter when it comes to >> accessing >> >> >> >> > private >> >> >> >> > state? >> >> >> >> > >> >> >> >> > ```js >> >> >> >> > const pattern = /foo/; >> >> >> >> > Reflect.setPrototypeOf(pattern, Date.prototype); >> >> >> >> > pattern instanceof Date; // true >> >> >> >> > pattern instanceof RegExp; // false >> >> >> >> > pattern.getMinutes(); // throws TypeError because [[DateValue]] >> >> >> >> > slot >> >> >> >> > is >> >> >> >> > missing >> >> >> >> > RegExp.prototype.exec.call(pattern, 'foo'); // works; object >> has >> >> >> >> > RegExp >> >> >> >> > private slots >> >> >> >> > ``` >> >> >> >> > >> >> >> >> >> If I removed that requirement, it would work. However, >> there'd be >> >> >> >> >> no >> >> >> >> >> way >> >> >> >> >> to keep the private data from being leaked. Sadly, it's all or >> >> >> >> >> nothing >> >> >> >> >> with >> >> >> >> >> this approach. Hard private or soft private, those are the >> only >> >> >> >> >> choices. >> >> >> >> > >> >> >> >> > In the context of what you’ve described here this may be true, >> but >> >> >> >> > no >> >> >> >> > such >> >> >> >> > limitation presently exists. We can already do all this — hard, >> >> >> >> > leak-free >> >> >> >> > privacy, brandedness, “friends” etc — with scopes and WeakMaps, >> >> >> >> > but >> >> >> >> > for >> >> >> >> > the >> >> >> >> > fact that the `WeakMap` intrinsics may be forged. So what’s >> >> >> >> > baffled >> >> >> >> > me >> >> >> >> > is >> >> >> >> > this: why are all the proposals exploring this space not >> >> >> >> > addressing >> >> >> >> > that >> >> >> >> > relatively simple existing problem, and instead starting off >> from >> >> >> >> > a >> >> >> >> > place of >> >> >> >> > significant new complexity? You said “maybe after the private >> >> >> >> > fields >> >> >> >> > problem >> >> >> >> > has been resolved, someone will figure out a better way to >> handle >> >> >> >> > your >> >> >> >> > use >> >> >> >> > cases,” but I’d have hoped for the opposite — I want the >> primitive >> >> >> >> > building >> >> >> >> > blocks which things like class field syntax could be built >> over, >> >> >> >> > if >> >> >> >> > it >> >> >> >> > is >> >> >> >> > found that they are still necessary once the root issue is >> solved >> >> >> >> > for. >> >> >> >> > >> >> >> >> >> The main reason the privacy is set on a declaration level is >> >> >> >> >> because >> >> >> >> >> scope-level inheritance isn't very good for class-oriented >> >> >> >> >> inheritance. >> >> >> >> > >> >> >> >> > Can you explain this more? I’m not sure what’s meant by >> >> >> >> > “scope-level >> >> >> >> > inheritance” here. >> >> >> >> > >> >> >> >> >> I don't intend to stop [...] >> >> >> >> > >> >> >> >> > I very much admire your dedication! I’m also digging the >> >> >> >> > discussion. >> >> >> >> > I >> >> >> >> > think >> >> >> >> > we may be representing viewpoints at opposite extremes here, so >> >> >> >> > it’s >> >> >> >> > an >> >> >> >> > interesting contrast, but it also probably means we may be >> lacking >> >> >> >> > some >> >> >> >> > context for understanding one another’s angles. I’d be curious >> to >> >> >> >> > hear >> >> >> >> > more >> >> >> >> > about what you see as the problems with the current fields >> >> >> >> > proposal + >> >> >> >> > how >> >> >> >> > your members proposal would solve them; the repo readme didn’t >> >> >> >> > seem >> >> >> >> > to >> >> >> >> > include a rationale section. >> >> >> >> > >> >> >> >> > On Sat, Jul 28, 2018 at 10:30 PM Ranando King < >> kingmph at gmail.com> >> >> >> >> > wrote: >> >> >> >> >> >> >> >> >> >> I've almost given up on making any significant headway in >> either >> >> >> >> >> adjusting >> >> >> >> >> or flat-out correcting the flaws in that proposal, but I don't >> >> >> >> >> intend >> >> >> >> >> to >> >> >> >> >> stop trying until either we get stuck with that proposal, or >> they >> >> >> >> >> understand >> >> >> >> >> and accept what I'm telling them, or logically prove that my >> >> >> >> >> concerns >> >> >> >> >> are >> >> >> >> >> either irrational or inconsequential. >> >> >> >> >> >> >> >> >> >> > Private object state in particular is only _made complex_ by >> >> >> >> >> > associating >> >> >> >> >> > it with declarations instead of scopes that happen to >> contain >> >> >> >> >> > declarations >> >> >> >> >> > (or into which constructors are passed, etc). The >> complexity is >> >> >> >> >> > artificial — >> >> >> >> >> > not a good sign imo. >> >> >> >> >> >> >> >> >> >> That's not quite right. What you're essentially asking for is >> a >> >> >> >> >> violatable >> >> >> >> >> private field, or as has been described by others, a "soft >> >> >> >> >> private". >> >> >> >> >> Since >> >> >> >> >> we agree that the "friendly" & "befriend" pair is a somewhat >> (if >> >> >> >> >> not >> >> >> >> >> completely) bad idea, I'm going to take 1 more pass at your 3 >> >> >> >> >> requests >> >> >> >> >> with >> >> >> >> >> a different angle. >> >> >> >> >> >> >> >> >> >> > Adding the same “slot” to multiple classes which don’t >> inherit >> >> >> >> >> > from >> >> >> >> >> > each >> >> >> >> >> > other >> >> >> >> >> > Selectively sharing access to private state through >> functions >> >> >> >> >> > declared >> >> >> >> >> > outside the class body >> >> >> >> >> >> >> >> >> >> ```js >> >> >> >> >> //Using my proposal >> >> >> >> >> var {A, B, C} = (() => { >> >> >> >> >> const common = Symbol("common"); >> >> >> >> >> >> >> >> >> >> class A { >> >> >> >> >> private [common] = 1; >> >> >> >> >> add(...args) { >> >> >> >> >> var retval = this#[common]; >> >> >> >> >> for (let obj of args) { >> >> >> >> >> retval += obj#[common]; >> >> >> >> >> } >> >> >> >> >> return retval; >> >> >> >> >> } >> >> >> >> >> } >> >> >> >> >> class B { >> >> >> >> >> private [common] = 2; >> >> >> >> >> optional() { >> >> >> >> >> console.log(`common member = ${this#[common]}`); >> >> >> >> >> } >> >> >> >> >> } >> >> >> >> >> var C = { >> >> >> >> >> private [common]: 3, >> >> >> >> >> required() { >> >> >> >> >> console.log(`common member = ${this#[common]}`); >> >> >> >> >> } >> >> >> >> >> } >> >> >> >> >> >> >> >> >> >> return { A, B, C }; >> >> >> >> >> })(); >> >> >> >> >> >> >> >> >> >> //So you want the following statement to not throw a TypeError >> >> >> >> >> and >> >> >> >> >> return >> >> >> >> >> 6 >> >> >> >> >> (new A()).add(new B(), C); >> >> >> >> >> ``` >> >> >> >> >> I'm not sure I can make this work in my proposal, and I'm >> >> >> >> >> absolutely >> >> >> >> >> sure >> >> >> >> >> you'd be flatly refused by the other proposal. If a `Symbol` >> is >> >> >> >> >> provided as >> >> >> >> >> the `[[IdentifierName]]` of a private or protected field, >> then I >> >> >> >> >> can >> >> >> >> >> let >> >> >> >> >> that `Symbol` be both the key and value that are added to the >> >> >> >> >> `[[DeclarationInfo]]` and `[[InheritanceInfo]]` records. That >> way >> >> >> >> >> there >> >> >> >> >> will >> >> >> >> >> be a common private field name usable by all 3 objects. >> However, >> >> >> >> >> the >> >> >> >> >> guardian logic tries to verify that the function trying to >> access >> >> >> >> >> the >> >> >> >> >> private fields of an instance is a member of the same or >> >> >> >> >> descending >> >> >> >> >> prototype that was used to create that instance. If I removed >> >> >> >> >> that >> >> >> >> >> requirement, it would work. However, there'd be no way to keep >> >> >> >> >> the >> >> >> >> >> private >> >> >> >> >> data from being leaked. Sadly, it's all or nothing with this >> >> >> >> >> approach. >> >> >> >> >> Hard >> >> >> >> >> private or soft private, those are the only choices. The TC39 >> >> >> >> >> board >> >> >> >> >> has >> >> >> >> >> already decided that what they want new syntax for is hard >> >> >> >> >> private. >> >> >> >> >> >> >> >> >> >> > Adding slots dynamically, e.g. when adding mix-in methods >> that >> >> >> >> >> > may >> >> >> >> >> > initialize a new slot if necessary when called, since >> >> >> >> >> > subclassing >> >> >> >> >> > is >> >> >> >> >> > not >> >> >> >> >> > always appropriate >> >> >> >> >> >> >> >> >> >> Because the TC39 board has set their sights on hard private, >> this >> >> >> >> >> will >> >> >> >> >> require new syntax like what I suggested earlier Adding >> private >> >> >> >> >> members >> >> >> >> >> dynamically would also pose a leak risk if it could be done >> after >> >> >> >> >> the >> >> >> >> >> prototype has been fully constructed. The main reason the >> privacy >> >> >> >> >> is >> >> >> >> >> set on >> >> >> >> >> a declaration level is because scope-level inheritance isn't >> very >> >> >> >> >> good >> >> >> >> >> for >> >> >> >> >> `class`-oriented inheritance. The `class` keyword was >> provided to >> >> >> >> >> simplify >> >> >> >> >> the vertical inheritance model, along with some API to enable >> >> >> >> >> inheritance >> >> >> >> >> from native objects even without using `class`. The syntax >> >> >> >> >> changes >> >> >> >> >> for >> >> >> >> >> simplifying private field declaration are just an extension of >> >> >> >> >> that. >> >> >> >> >> Even >> >> >> >> >> though it's not unusual for some developers to spend a lot of >> >> >> >> >> time >> >> >> >> >> working >> >> >> >> >> with fringe use-cases, syntax changes are almost always going >> to >> >> >> >> >> be >> >> >> >> >> made for >> >> >> >> >> the most common use cases first. Maybe after the private >> fields >> >> >> >> >> problem >> >> >> >> >> has >> >> >> >> >> been resolved, someone will figure out a better way to handle >> >> >> >> >> your >> >> >> >> >> use >> >> >> >> >> cases. >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> On Sat, Jul 28, 2018 at 3:52 PM Darien Valentine >> >> >> >> >> <valentinium at gmail.com> >> >> >> >> >> wrote: >> >> >> >> >>> >> >> >> >> >>> > Are you saying you want multiple non-hierarchally related >> >> >> >> >>> > classes >> >> >> >> >>> > to >> >> >> >> >>> > have an instance private field with shared name [...] >> >> >> >> >>> >> >> >> >> >>> Yeah. This is a hard problem to solve when trying to >> integrate >> >> >> >> >>> private >> >> >> >> >>> fields with class syntax, but it’s not a problem at all when >> >> >> >> >>> privacy >> >> >> >> >>> is a >> >> >> >> >>> more generic tool based on scope. This also isn’t a foreign >> >> >> >> >>> concept >> >> >> >> >>> in >> >> >> >> >>> ES: >> >> >> >> >>> consider this intrinsic method: >> >> >> >> >>> >> >> >> >> >>> https://tc39.github.io/ecma262/#sec-arraybuffer.isview >> >> >> >> >>> >> >> >> >> >>> This method returns true if the argument has the >> >> >> >> >>> `[[ViewedArrayBuffer]]` >> >> >> >> >>> slot. This slot exists on genuine instances of both >> >> >> >> >>> `%TypedArray%` >> >> >> >> >>> and >> >> >> >> >>> `%DataView%`, but they do not receive these slots by way of >> >> >> >> >>> inheritance from >> >> >> >> >>> a common constructor. There are similar cases in HTML host >> APIs. >> >> >> >> >>> >> >> >> >> >>> > The befriend keyword would allow an object to request >> >> >> >> >>> > friendship >> >> >> >> >>> > with >> >> >> >> >>> > an existing friendly object. I'm not sure this is a good >> idea, >> >> >> >> >>> > though. >> >> >> >> >>> >> >> >> >> >>> I don’t think it is either, no. It’s too much complexity for >> too >> >> >> >> >>> little >> >> >> >> >>> gain. But again, this is achievable “for free” just by >> divorcing >> >> >> >> >>> “private >> >> >> >> >>> object state” from class declarations (or object literals). I >> >> >> >> >>> would >> >> >> >> >>> ask: >> >> >> >> >>> what problem is solved by making this a feature of the >> >> >> >> >>> declarations >> >> >> >> >>> themselves? Does it merit the complexity and the hoop jumping >> >> >> >> >>> needed >> >> >> >> >>> to >> >> >> >> >>> handle edge cases?\* >> >> >> >> >>> >> >> >> >> >>> \* One person’s edge case; another’s everyday concern haha. >> >> >> >> >>> >> >> >> >> >>> > The example you gave above still declares the functions in >> >> >> >> >>> > question >> >> >> >> >>> > inside the class body, so that's not really a solution. >> >> >> >> >>> >> >> >> >> >>> If you’re referring to the first example, that is a >> >> >> >> >>> demonstration >> >> >> >> >>> of >> >> >> >> >>> what >> >> >> >> >>> is possible using the existing stage 3 class fields proposal >> as >> >> >> >> >>> implemented >> >> >> >> >>> in Chrome. It isn’t what I want; it’s what’s necessary to >> >> >> >> >>> achieve >> >> >> >> >>> this >> >> >> >> >>> with >> >> >> >> >>> the current stage 3 proposed model. >> >> >> >> >>> >> >> >> >> >>> > Sounds to me like you'd love for class syntax to look like >> >> >> >> >>> > this >> >> >> >> >>> > [[example with mixin syntax in declaration]] >> >> >> >> >>> >> >> >> >> >>> Perhaps — it’s interesting for sure! But the pattern that >> >> >> >> >>> already >> >> >> >> >>> works, >> >> >> >> >>> `mixin(Cstr)`, is not presently a source of problems for me. >> >> >> >> >>> Private >> >> >> >> >>> object >> >> >> >> >>> state in particular is only _made complex_ by associating it >> >> >> >> >>> with >> >> >> >> >>> declarations instead of scopes that happen to contain >> >> >> >> >>> declarations >> >> >> >> >>> (or >> >> >> >> >>> into >> >> >> >> >>> which constructors are passed, etc). The complexity is >> >> >> >> >>> artificial — >> >> >> >> >>> not a >> >> >> >> >>> good sign imo. >> >> >> >> >>> >> >> >> >> >>> > One thing both proposal-class-fields and >> >> >> >> >>> > proposal-object-members >> >> >> >> >>> > have >> >> >> >> >>> > in common is that the focus is on producing >> instance-private >> >> >> >> >>> > fields. >> >> >> >> >>> > All 3 >> >> >> >> >>> > of the scenarios you presented lay outside of that focus >> for >> >> >> >> >>> > one >> >> >> >> >>> > reason or >> >> >> >> >>> > another. >> >> >> >> >>> >> >> >> >> >>> Both the WeakMap solution and the stub concept I provided >> after >> >> >> >> >>> are >> >> >> >> >>> more >> >> >> >> >>> generic than privacy in either of those proposals. When I say >> >> >> >> >>> "object >> >> >> >> >>> private state," it’s true that the object in question could >> be >> >> >> >> >>> any >> >> >> >> >>> object. >> >> >> >> >>> But in practice, any realization of the feature would pertain >> >> >> >> >>> chiefly >> >> >> >> >>> to >> >> >> >> >>> class instances, and the examples I gave, though contrived, >> do >> >> >> >> >>> concern >> >> >> >> >>> class >> >> >> >> >>> instances. The reason private object state is chiefly an >> issue >> >> >> >> >>> of >> >> >> >> >>> class >> >> >> >> >>> instances stems directly from the nature of prototype methods >> >> >> >> >>> and >> >> >> >> >>> accessors, >> >> >> >> >>> so if you are not making use of prototypes, you could instead >> >> >> >> >>> have >> >> >> >> >>> used a >> >> >> >> >>> closure+factory directly. >> >> >> >> >>> >> >> >> >> >>> --- >> >> >> >> >>> >> >> >> >> >>> In a nutshell, my issue with existing proposals could >> probably >> >> >> >> >>> be >> >> >> >> >>> summarized as a concern that they are neither as generic nor >> as >> >> >> >> >>> simple >> >> >> >> >>> as >> >> >> >> >>> native slots. To be clear, proper “slots” are an internal >> >> >> >> >>> concept, >> >> >> >> >>> only >> >> >> >> >>> observable indirectly — but they are the special sauce >> >> >> >> >>> underlying a >> >> >> >> >>> number >> >> >> >> >>> of behaviors which are presently awkward to achieve in ES >> code >> >> >> >> >>> itself, >> >> >> >> >>> and >> >> >> >> >>> they are a nice simple model of private object state which is >> >> >> >> >>> tantalizingly >> >> >> >> >>> close to, but not _exactly_ the same as in two critical ways, >> >> >> >> >>> symbol >> >> >> >> >>> keyed >> >> >> >> >>> properties. That said, “real” slots would continue to have an >> >> >> >> >>> advantage with >> >> >> >> >>> regard to cross-realm stuff even if private symbol keys >> existed. >> >> >> >> >>> >> >> >> >> >>> That such a model is radically simpler — minmax and all that >> — >> >> >> >> >>> feels >> >> >> >> >>> very >> >> >> >> >>> important to me, but I dunno. I’m not holding my breath for >> big >> >> >> >> >>> changes >> >> >> >> >>> here. The current stage 3 proposal seems to be unstoppable; >> much >> >> >> >> >>> smarter / >> >> >> >> >>> more important people than me have already tried and failed. >> :) >> >> >> >> >>> >> >> >> >> >>> >> >> >> >> >>> On Sat, Jul 28, 2018 at 3:14 PM Ranando King < >> kingmph at gmail.com> >> >> >> >> >>> wrote: >> >> >> >> >>>> >> >> >> >> >>>> In a word... wow. You've got me thinking hard here. Those >> are >> >> >> >> >>>> some >> >> >> >> >>>> peculiar use cases, and they do a great job of highlighting >> why >> >> >> >> >>>> someone >> >> >> >> >>>> might forego using `class`. One thing both >> >> >> >> >>>> proposal-class-fields >> >> >> >> >>>> and >> >> >> >> >>>> proposal-object-members have in common is that the focus is >> on >> >> >> >> >>>> producing >> >> >> >> >>>> instance-private fields. All 3 of the scenarios you >> presented >> >> >> >> >>>> lay >> >> >> >> >>>> outside of >> >> >> >> >>>> that focus for one reason or another. >> >> >> >> >>>> >> >> >> >> >>>> > Adding the same “slot” to multiple classes which don’t >> >> >> >> >>>> > inherit >> >> >> >> >>>> > from >> >> >> >> >>>> > each other >> >> >> >> >>>> >> >> >> >> >>>> I'm a little confused by this one. Are you saying you want >> >> >> >> >>>> multiple >> >> >> >> >>>> non-hierarchally related classes to have an instance private >> >> >> >> >>>> field >> >> >> >> >>>> with >> >> >> >> >>>> shared name, such that the same private field name refers >> to a >> >> >> >> >>>> distinct and >> >> >> >> >>>> separate field on each instance of every such class, but >> where >> >> >> >> >>>> any >> >> >> >> >>>> such >> >> >> >> >>>> instance can have that field referenced by that shared name >> >> >> >> >>>> from >> >> >> >> >>>> any >> >> >> >> >>>> member >> >> >> >> >>>> function of the corresponding classes? (Wow that was wordy >> to >> >> >> >> >>>> write >> >> >> >> >>>> out...) >> >> >> >> >>>> If this is what you meant, you're describing friend classes. >> >> >> >> >>>> The >> >> >> >> >>>> top-down >> >> >> >> >>>> processing nature of ES makes this a difficult thing to >> create >> >> >> >> >>>> a >> >> >> >> >>>> clean >> >> >> >> >>>> syntax for without risking leaking the private state or >> >> >> >> >>>> fundamentally >> >> >> >> >>>> altering how ES is processed. Mutual friendship is even >> harder. >> >> >> >> >>>> >> >> >> >> >>>> ... and yet I just thought of a way to do it. By telling you >> >> >> >> >>>> this >> >> >> >> >>>> I'm >> >> >> >> >>>> leaving myself to consider writing a proposal containing 2 >> new >> >> >> >> >>>> keywords: >> >> >> >> >>>> `befriend` and `friendly`. I don't know if this can be done >> >> >> >> >>>> with >> >> >> >> >>>> the >> >> >> >> >>>> existing proposal being what it is. However, with my >> proposal, >> >> >> >> >>>> there's a >> >> >> >> >>>> chance. The `friendly` keyword would declare that an object >> is >> >> >> >> >>>> prepared to >> >> >> >> >>>> share select information with any object that befriends it. >> The >> >> >> >> >>>> `befriend` >> >> >> >> >>>> keyword would allow an object to request friendship with an >> >> >> >> >>>> existing >> >> >> >> >>>> friendly object. I'm not sure this is a good idea, though. >> This >> >> >> >> >>>> means >> >> >> >> >>>> that >> >> >> >> >>>> any object declared 'friendly' is automatically insecure as >> all >> >> >> >> >>>> it >> >> >> >> >>>> takes to >> >> >> >> >>>> gain access to the selected members of its private space >> would >> >> >> >> >>>> be >> >> >> >> >>>> to >> >> >> >> >>>> 'befriend' it. >> >> >> >> >>>> >> >> >> >> >>>> > Selectively sharing access to private state through >> functions >> >> >> >> >>>> > declared >> >> >> >> >>>> > outside the class body >> >> >> >> >>>> >> >> >> >> >>>> The example you gave above still declares the functions in >> >> >> >> >>>> question >> >> >> >> >>>> inside the `class` body, so that's not really a solution. If >> >> >> >> >>>> the >> >> >> >> >>>> example you >> >> >> >> >>>> gave actually solves your use case, then what you're asking >> for >> >> >> >> >>>> here >> >> >> >> >>>> isn't >> >> >> >> >>>> even needed. If, however, that was a bad example, then it >> >> >> >> >>>> sounds >> >> >> >> >>>> like >> >> >> >> >>>> you're >> >> >> >> >>>> looking for friend functions. See the previous section. >> >> >> >> >>>> >> >> >> >> >>>> > Adding slots dynamically, e.g. when adding mix-in methods >> >> >> >> >>>> > that >> >> >> >> >>>> > may >> >> >> >> >>>> > initialize a new slot if necessary when called, since >> >> >> >> >>>> > subclassing >> >> >> >> >>>> > is not >> >> >> >> >>>> > always appropriate >> >> >> >> >>>> >> >> >> >> >>>> Sounds to me like you'd love for `class` syntax to look like >> >> >> >> >>>> this: >> >> >> >> >>>> >> >> >> >> >>>> ```js >> >> >> >> >>>> class [<identifierName1>] [extends <identifierName2>] [mixes >> >> >> >> >>>> <identifierName3>[, <identifierName3>[, ...]]] { ... } >> >> >> >> >>>> ``` >> >> >> >> >>>> so that the private fields of the objects in the `mixes` >> list >> >> >> >> >>>> are >> >> >> >> >>>> added >> >> >> >> >>>> to the set of private fields provided by the `class` >> definition >> >> >> >> >>>> directly. >> >> >> >> >>>> That would also require another proposal, but I think that >> can >> >> >> >> >>>> be >> >> >> >> >>>> done >> >> >> >> >>>> regardless of which instance-private fields proposal gets >> >> >> >> >>>> accepted. >> >> >> >> >>>> >> >> >> >> >>>> On Sat, Jul 28, 2018 at 12:49 PM Darien Valentine >> >> >> >> >>>> <valentinium at gmail.com> wrote: >> >> >> >> >>>>> >> >> >> >> >>>>> To put this another, much briefer way, here’s a >> hypothetical >> >> >> >> >>>>> model >> >> >> >> >>>>> for >> >> >> >> >>>>> associating private state with objects that would cover me. >> >> >> >> >>>>> Privacy >> >> >> >> >>>>> would be >> >> >> >> >>>>> provided... >> >> >> >> >>>>> >> >> >> >> >>>>> 1. in the form of symbolic keys whose presence cannot be >> >> >> >> >>>>> observed >> >> >> >> >>>>> (i.e., they would not be exposed by >> `getOwnPropertySymbols`) >> >> >> >> >>>>> 2. and which have a syntactic declaration so that one can >> be >> >> >> >> >>>>> sure >> >> >> >> >>>>> they >> >> >> >> >>>>> are really getting private keys (i.e., an api like >> >> >> >> >>>>> `Symbol.private()` >> >> >> >> >>>>> wouldn’t work) >> >> >> >> >>>>> >> >> >> >> >>>>> ``` >> >> >> >> >>>>> const bar = private(); >> >> >> >> >>>>> >> >> >> >> >>>>> // alternatively: const #bar; could be anything so long as >> >> >> >> >>>>> it’s >> >> >> >> >>>>> syntactic >> >> >> >> >>>>> >> >> >> >> >>>>> class Foo { >> >> >> >> >>>>> constructor() { >> >> >> >> >>>>> this[bar] = 1; >> >> >> >> >>>>> } >> >> >> >> >>>>> } >> >> >> >> >>>>> >> >> >> >> >>>>> // etc >> >> >> >> >>>>> ``` >> >> >> >> >>>>> >> >> >> >> >>>>> The keys would be typeof 'symbol'; the only difference >> being >> >> >> >> >>>>> that >> >> >> >> >>>>> they >> >> >> >> >>>>> are symbols which are flagged as private when created. They >> >> >> >> >>>>> would >> >> >> >> >>>>> be >> >> >> >> >>>>> permitted only in syntactic property assignments and >> accesses. >> >> >> >> >>>>> Existing >> >> >> >> >>>>> reflection utilities would disallow the use or appearance >> of >> >> >> >> >>>>> such >> >> >> >> >>>>> symbols >> >> >> >> >>>>> both to ensure privacy and to maintain the invariant that >> they >> >> >> >> >>>>> are >> >> >> >> >>>>> always >> >> >> >> >>>>> simple data properties: >> >> >> >> >>>>> >> >> >> >> >>>>> ```js >> >> >> >> >>>>> Reflect.defineProperty({}, #bar, { ... }); // throws type >> >> >> >> >>>>> error >> >> >> >> >>>>> Object.getOwnPropertyDescriptors(someObjWithAPrivateSlot); >> // >> >> >> >> >>>>> does >> >> >> >> >>>>> not >> >> >> >> >>>>> include it >> >> >> >> >>>>> foo[bar] = 2; // fine >> >> >> >> >>>>> ``` >> >> >> >> >>>>> >> >> >> >> >>>>> This is significantly simpler than what’s in flight both in >> >> >> >> >>>>> terms >> >> >> >> >>>>> of >> >> >> >> >>>>> syntax and mechanics, which makes me suspicious that I’m >> >> >> >> >>>>> probably >> >> >> >> >>>>> ignoring >> >> >> >> >>>>> things that other people find important. However it would >> >> >> >> >>>>> bring >> >> >> >> >>>>> parity to ES >> >> >> >> >>>>> objects wrt being able to implement genuinely private >> slots in >> >> >> >> >>>>> userland with >> >> >> >> >>>>> the same flexibility as what is done internally. >> >> >> >> >>>>> >> >> >> >> >>>>> In total, this entails a new primary expression, a boolean >> >> >> >> >>>>> flag >> >> >> >> >>>>> associated with symbol values, and an extra step added to >> >> >> >> >>>>> several >> >> >> >> >>>>> algorithms >> >> >> >> >>>>> associated with Object and Reflect. >> >> >> >> >>>>> _______________________________________________ >> >> >> >> >>>>> 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/20180730/db0a9426/attachment-0001.html>
On 07/29/2018 04:37 PM, Isiah Meadows wrote:
BTW, I came up with an alternate proposal for privacy altogether: tc39/proposal-class-fields#115
TL;DR: private symbols that proxies can't see and that can't be enumerated.
Aside from syntax, the main semantic difference I see between this alternative and the main one is that this alternative defines private fields as expandos, creating opportunities for mischief by attaching them to unexpected objects. Aside from privacy, one of the things the private fields proposal gives you is consistency among multiple private fields on the same object. In the rare cases where you don't want that, you could use weak maps.
Waldemar
On 07/29/2018 04:37 PM, Isiah Meadows wrote: > BTW, I came up with an alternate proposal for privacy altogether: > https://github.com/tc39/proposal-class-fields/issues/115 > > TL;DR: private symbols that proxies can't see and that can't be enumerated. Aside from syntax, the main semantic difference I see between this alternative and the main one is that this alternative defines private fields as expandos, creating opportunities for mischief by attaching them to unexpected objects. Aside from privacy, one of the things the private fields proposal gives you is consistency among multiple private fields on the same object. In the rare cases where you don't want that, you could use weak maps. Waldemar
First, my private symbols are properly private. The only "unexpected" thing that could happen is making an object larger memory-wise, which engines already have to be equipped to handle now (libraries aren't always well-behaved, and like to occasionally add expando properties to builtins and DOM elements). About the only thing most people would care about is in the debugger.
Second, I had things like this in mind with supporting expando properties: nodejs/node/blob/ae4fde8bc883686def5badfb324236320669e8f4/lib/internal/linkedlist.js
In that case, the Node.js people made it a pseudo-mixin rather than an actual type for performance reasons - there's fewer object allocations and they needed that.
So I've considered the expando problem, and I disagree about it being a problem at all.
Isiah Meadows contact at isiahmeadows.com, www.isiahmeadows.com
First, my private symbols are properly *private*. The only "unexpected" thing that could happen is making an object larger memory-wise, which engines already have to be equipped to handle now (libraries aren't always well-behaved, and like to occasionally add expando properties to builtins and DOM elements). About the only thing most people would care about is in the debugger. Second, I had things like this in mind with supporting expando properties: https://github.com/nodejs/node/blob/ae4fde8bc883686def5badfb324236320669e8f4/lib/internal/linkedlist.js In that case, the Node.js people made it a pseudo-mixin rather than an actual type for performance reasons - there's fewer object allocations and they needed that. So I've considered the expando problem, and I disagree about it being a problem at all. ----- Isiah Meadows contact at isiahmeadows.com www.isiahmeadows.com On Mon, Jul 30, 2018 at 6:35 PM, Waldemar Horwat <waldemar at google.com> wrote: > On 07/29/2018 04:37 PM, Isiah Meadows wrote: >> >> BTW, I came up with an alternate proposal for privacy altogether: >> https://github.com/tc39/proposal-class-fields/issues/115 >> >> TL;DR: private symbols that proxies can't see and that can't be >> enumerated. > > > Aside from syntax, the main semantic difference I see between this > alternative and the main one is that this alternative defines private fields > as expandos, creating opportunities for mischief by attaching them to > unexpected objects. Aside from privacy, one of the things the private > fields proposal gives you is consistency among multiple private fields on > the same object. In the rare cases where you don't want that, you could use > weak maps. > > Waldemar
So you're wanting the ability for a 3rd-party function to be able to store data private to that library on an object it didn't create, and that only that library can access?
So you're wanting the ability for a 3rd-party function to be able to store data private to that library on an object it didn't create, and that only that library can access? On Mon, Jul 30, 2018 at 6:36 PM Isiah Meadows <isiahmeadows at gmail.com> wrote: > First, my private symbols are properly *private*. The only > "unexpected" thing that could happen is making an object larger > memory-wise, which engines already have to be equipped to handle now > (libraries aren't always well-behaved, and like to occasionally add > expando properties to builtins and DOM elements). About the only thing > most people would care about is in the debugger. > > Second, I had things like this in mind with supporting expando > properties: > https://github.com/nodejs/node/blob/ae4fde8bc883686def5badfb324236320669e8f4/lib/internal/linkedlist.js > > In that case, the Node.js people made it a pseudo-mixin rather than an > actual type for performance reasons - there's fewer object allocations > and they needed that. > > So I've considered the expando problem, and I disagree about it being > a problem at all. > > ----- > > Isiah Meadows > contact at isiahmeadows.com > www.isiahmeadows.com > > > On Mon, Jul 30, 2018 at 6:35 PM, Waldemar Horwat <waldemar at google.com> > wrote: > > On 07/29/2018 04:37 PM, Isiah Meadows wrote: > >> > >> BTW, I came up with an alternate proposal for privacy altogether: > >> https://github.com/tc39/proposal-class-fields/issues/115 > >> > >> TL;DR: private symbols that proxies can't see and that can't be > >> enumerated. > > > > > > Aside from syntax, the main semantic difference I see between this > > alternative and the main one is that this alternative defines private > fields > > as expandos, creating opportunities for mischief by attaching them to > > unexpected objects. Aside from privacy, one of the things the private > > fields proposal gives you is consistency among multiple private fields on > > the same object. In the rare cases where you don't want that, you could > use > > weak maps. > > > > Waldemar > _______________________________________________ > 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/20180730/475f06f0/attachment-0001.html>
That is one supported use case, yes. But that isn't the only use case this supports. It can still extend to traditional private class data, too.
Isiah Meadows contact at isiahmeadows.com, www.isiahmeadows.com
That is one supported use case, yes. But that isn't the only use case this supports. It can still extend to traditional private class data, too. ----- Isiah Meadows contact at isiahmeadows.com www.isiahmeadows.com On Mon, Jul 30, 2018 at 8:04 PM, Ranando King <kingmph at gmail.com> wrote: > So you're wanting the ability for a 3rd-party function to be able to store > data private to that library on an object it didn't create, and that only > that library can access? > > On Mon, Jul 30, 2018 at 6:36 PM Isiah Meadows <isiahmeadows at gmail.com> > wrote: >> >> First, my private symbols are properly *private*. The only >> "unexpected" thing that could happen is making an object larger >> memory-wise, which engines already have to be equipped to handle now >> (libraries aren't always well-behaved, and like to occasionally add >> expando properties to builtins and DOM elements). About the only thing >> most people would care about is in the debugger. >> >> Second, I had things like this in mind with supporting expando >> properties: >> https://github.com/nodejs/node/blob/ae4fde8bc883686def5badfb324236320669e8f4/lib/internal/linkedlist.js >> >> In that case, the Node.js people made it a pseudo-mixin rather than an >> actual type for performance reasons - there's fewer object allocations >> and they needed that. >> >> So I've considered the expando problem, and I disagree about it being >> a problem at all. >> >> ----- >> >> Isiah Meadows >> contact at isiahmeadows.com >> www.isiahmeadows.com >> >> >> On Mon, Jul 30, 2018 at 6:35 PM, Waldemar Horwat <waldemar at google.com> >> wrote: >> > On 07/29/2018 04:37 PM, Isiah Meadows wrote: >> >> >> >> BTW, I came up with an alternate proposal for privacy altogether: >> >> https://github.com/tc39/proposal-class-fields/issues/115 >> >> >> >> TL;DR: private symbols that proxies can't see and that can't be >> >> enumerated. >> > >> > >> > Aside from syntax, the main semantic difference I see between this >> > alternative and the main one is that this alternative defines private >> > fields >> > as expandos, creating opportunities for mischief by attaching them to >> > unexpected objects. Aside from privacy, one of the things the private >> > fields proposal gives you is consistency among multiple private fields >> > on >> > the same object. In the rare cases where you don't want that, you could >> > use >> > weak maps. >> > >> > Waldemar >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss
Just that use case alone is problematic. If the 3rd party function is not extensible, then the new private data should not be allowed. If the library cannot function without storing that data, then the function will have no choice but to fall back to WeakMaps which don't care if the key is not extensible. So why not just stick with WeakMaps for that case? And if that's the case, then there would be little need for so open a means of defining private field names. The proposal I'm offering offers the room to extend it in the future to support everything else you might look for from your private symbols idea.... unless you think I missed something.
Just that use case alone is problematic. If the 3rd party function is not extensible, then the new private data should not be allowed. If the library cannot function without storing that data, then the function will have no choice but to fall back to WeakMaps which don't care if the key is not extensible. So why not just stick with WeakMaps for that case? And if that's the case, then there would be little need for so open a means of defining private field names. The proposal I'm offering offers the room to extend it in the future to support everything else you might look for from your private symbols idea.... unless you think I missed something. On Mon, Jul 30, 2018 at 7:26 PM Isiah Meadows <isiahmeadows at gmail.com> wrote: > That is one supported use case, yes. But that isn't the only use case > this supports. It can still extend to traditional private class data, > too. > > ----- > > Isiah Meadows > contact at isiahmeadows.com > www.isiahmeadows.com > > > On Mon, Jul 30, 2018 at 8:04 PM, Ranando King <kingmph at gmail.com> wrote: > > So you're wanting the ability for a 3rd-party function to be able to > store > > data private to that library on an object it didn't create, and that only > > that library can access? > > > > On Mon, Jul 30, 2018 at 6:36 PM Isiah Meadows <isiahmeadows at gmail.com> > > wrote: > >> > >> First, my private symbols are properly *private*. The only > >> "unexpected" thing that could happen is making an object larger > >> memory-wise, which engines already have to be equipped to handle now > >> (libraries aren't always well-behaved, and like to occasionally add > >> expando properties to builtins and DOM elements). About the only thing > >> most people would care about is in the debugger. > >> > >> Second, I had things like this in mind with supporting expando > >> properties: > >> > https://github.com/nodejs/node/blob/ae4fde8bc883686def5badfb324236320669e8f4/lib/internal/linkedlist.js > >> > >> In that case, the Node.js people made it a pseudo-mixin rather than an > >> actual type for performance reasons - there's fewer object allocations > >> and they needed that. > >> > >> So I've considered the expando problem, and I disagree about it being > >> a problem at all. > >> > >> ----- > >> > >> Isiah Meadows > >> contact at isiahmeadows.com > >> www.isiahmeadows.com > >> > >> > >> On Mon, Jul 30, 2018 at 6:35 PM, Waldemar Horwat <waldemar at google.com> > >> wrote: > >> > On 07/29/2018 04:37 PM, Isiah Meadows wrote: > >> >> > >> >> BTW, I came up with an alternate proposal for privacy altogether: > >> >> https://github.com/tc39/proposal-class-fields/issues/115 > >> >> > >> >> TL;DR: private symbols that proxies can't see and that can't be > >> >> enumerated. > >> > > >> > > >> > Aside from syntax, the main semantic difference I see between this > >> > alternative and the main one is that this alternative defines private > >> > fields > >> > as expandos, creating opportunities for mischief by attaching them to > >> > unexpected objects. Aside from privacy, one of the things the private > >> > fields proposal gives you is consistency among multiple private fields > >> > on > >> > the same object. In the rare cases where you don't want that, you > could > >> > use > >> > weak maps. > >> > > >> > Waldemar > >> _______________________________________________ > >> 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/20180730/edb7960a/attachment.html>
I meant to say if the object passed to the 3rd party function.....
I meant to say if the object passed to the 3rd party function..... On Mon, Jul 30, 2018 at 7:59 PM Ranando King <kingmph at gmail.com> wrote: > Just that use case alone is problematic. If the 3rd party function is not > extensible, then the new private data should not be allowed. If the library > cannot function without storing that data, then the function will have no > choice but to fall back to WeakMaps which don't care if the key is not > extensible. So why not just stick with WeakMaps for that case? And if > that's the case, then there would be little need for so open a means of > defining private field names. The proposal I'm offering offers the room to > extend it in the future to support everything else you might look for from > your private symbols idea.... unless you think I missed something. > > On Mon, Jul 30, 2018 at 7:26 PM Isiah Meadows <isiahmeadows at gmail.com> > wrote: > >> That is one supported use case, yes. But that isn't the only use case >> this supports. It can still extend to traditional private class data, >> too. >> >> ----- >> >> Isiah Meadows >> contact at isiahmeadows.com >> www.isiahmeadows.com >> >> >> On Mon, Jul 30, 2018 at 8:04 PM, Ranando King <kingmph at gmail.com> wrote: >> > So you're wanting the ability for a 3rd-party function to be able to >> store >> > data private to that library on an object it didn't create, and that >> only >> > that library can access? >> > >> > On Mon, Jul 30, 2018 at 6:36 PM Isiah Meadows <isiahmeadows at gmail.com> >> > wrote: >> >> >> >> First, my private symbols are properly *private*. The only >> >> "unexpected" thing that could happen is making an object larger >> >> memory-wise, which engines already have to be equipped to handle now >> >> (libraries aren't always well-behaved, and like to occasionally add >> >> expando properties to builtins and DOM elements). About the only thing >> >> most people would care about is in the debugger. >> >> >> >> Second, I had things like this in mind with supporting expando >> >> properties: >> >> >> https://github.com/nodejs/node/blob/ae4fde8bc883686def5badfb324236320669e8f4/lib/internal/linkedlist.js >> >> >> >> In that case, the Node.js people made it a pseudo-mixin rather than an >> >> actual type for performance reasons - there's fewer object allocations >> >> and they needed that. >> >> >> >> So I've considered the expando problem, and I disagree about it being >> >> a problem at all. >> >> >> >> ----- >> >> >> >> Isiah Meadows >> >> contact at isiahmeadows.com >> >> www.isiahmeadows.com >> >> >> >> >> >> On Mon, Jul 30, 2018 at 6:35 PM, Waldemar Horwat <waldemar at google.com> >> >> wrote: >> >> > On 07/29/2018 04:37 PM, Isiah Meadows wrote: >> >> >> >> >> >> BTW, I came up with an alternate proposal for privacy altogether: >> >> >> https://github.com/tc39/proposal-class-fields/issues/115 >> >> >> >> >> >> TL;DR: private symbols that proxies can't see and that can't be >> >> >> enumerated. >> >> > >> >> > >> >> > Aside from syntax, the main semantic difference I see between this >> >> > alternative and the main one is that this alternative defines private >> >> > fields >> >> > as expandos, creating opportunities for mischief by attaching them to >> >> > unexpected objects. Aside from privacy, one of the things the >> private >> >> > fields proposal gives you is consistency among multiple private >> fields >> >> > on >> >> > the same object. In the rare cases where you don't want that, you >> could >> >> > use >> >> > weak maps. >> >> > >> >> > Waldemar >> >> _______________________________________________ >> >> 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/20180730/9e0327bd/attachment-0001.html>
The reason private symbols are appropriate for Node's use case is because it's conceptually a mixin, not a simple key/value map with various utility functions (and weak map lookup is slower than property access). JSDOM uses a similar utility 1 as a sort of mixin.
Keep in mind, I'm specifically against the abuse of weak maps for private state that's conceptually (in an abstract sense, not runtime) part of an object. Weak maps make sense when the weak map is the dictionary conceptually (think: caching). But if conceptually, the object is the dictionary, putting it in a weak map is giving the engine the wrong info - properties have inline caches and heavy optimization, but you can't do the same for weak maps in the other direction without literally implementing them as properties. (I would love to be proven wrong here, BTW.)
Let me draw a quick comparison: When do you use a map/set with string keys, and when do you use an object instead?
- Both are functionally equivalent, but engines use very different algorithms for each one.
- I can almost guarantee you don't use maps when object properties work.
One last thing: how would you hope to deal with module-internal data stored on arbitrary objects, using any means other than private symbols or something similar? To clarify, I'm talking of opaque object structs 2, not simply classes. (BTW, that one is easier to manage as a struct rather than a class, because of how many "methods" there are operating on the state.)
Isiah Meadows contact at isiahmeadows.com, www.isiahmeadows.com
The reason private symbols are appropriate for Node's use case is because it's conceptually a mixin, not a simple key/value map with various utility functions (and weak map lookup is slower than property access). JSDOM uses a similar utility [1] as a sort of mixin. Keep in mind, I'm specifically *against* the abuse of weak maps for private state that's conceptually (in an abstract sense, not runtime) part of an object. Weak maps make sense when the weak map is the dictionary conceptually (think: caching). But if conceptually, the object is the dictionary, putting it in a weak map is giving the engine the wrong info - properties have inline caches and heavy optimization, but you can't do the same for weak maps in the other direction without literally implementing them as properties. (I would *love* to be proven wrong here, BTW.) Let me draw a quick comparison: When do you use a map/set with string keys, and when do you use an object instead? - Both are functionally equivalent, but engines use *very* different algorithms for each one. - I can almost guarantee you don't use maps when object properties work. One last thing: how would you hope to deal with module-internal data stored on arbitrary objects, using any means other than private symbols or something similar? To clarify, I'm talking of opaque object structs [2], not simply classes. (BTW, that one is easier to manage as a struct rather than a class, because of how many "methods" there are operating on the state.) [1]: https://github.com/jsdom/js-symbol-tree [2]: https://github.com/isiahmeadows/enigma/blob/master/src/parser.ts ----- Isiah Meadows contact at isiahmeadows.com www.isiahmeadows.com On Mon, Jul 30, 2018 at 9:00 PM, Ranando King <kingmph at gmail.com> wrote: > I meant to say if the object passed to the 3rd party function..... > > > On Mon, Jul 30, 2018 at 7:59 PM Ranando King <kingmph at gmail.com> wrote: >> >> Just that use case alone is problematic. If the 3rd party function is not >> extensible, then the new private data should not be allowed. If the library >> cannot function without storing that data, then the function will have no >> choice but to fall back to WeakMaps which don't care if the key is not >> extensible. So why not just stick with WeakMaps for that case? And if that's >> the case, then there would be little need for so open a means of defining >> private field names. The proposal I'm offering offers the room to extend it >> in the future to support everything else you might look for from your >> private symbols idea.... unless you think I missed something. >> >> On Mon, Jul 30, 2018 at 7:26 PM Isiah Meadows <isiahmeadows at gmail.com> >> wrote: >>> >>> That is one supported use case, yes. But that isn't the only use case >>> this supports. It can still extend to traditional private class data, >>> too. >>> >>> ----- >>> >>> Isiah Meadows >>> contact at isiahmeadows.com >>> www.isiahmeadows.com >>> >>> >>> On Mon, Jul 30, 2018 at 8:04 PM, Ranando King <kingmph at gmail.com> wrote: >>> > So you're wanting the ability for a 3rd-party function to be able to >>> > store >>> > data private to that library on an object it didn't create, and that >>> > only >>> > that library can access? >>> > >>> > On Mon, Jul 30, 2018 at 6:36 PM Isiah Meadows <isiahmeadows at gmail.com> >>> > wrote: >>> >> >>> >> First, my private symbols are properly *private*. The only >>> >> "unexpected" thing that could happen is making an object larger >>> >> memory-wise, which engines already have to be equipped to handle now >>> >> (libraries aren't always well-behaved, and like to occasionally add >>> >> expando properties to builtins and DOM elements). About the only thing >>> >> most people would care about is in the debugger. >>> >> >>> >> Second, I had things like this in mind with supporting expando >>> >> properties: >>> >> >>> >> https://github.com/nodejs/node/blob/ae4fde8bc883686def5badfb324236320669e8f4/lib/internal/linkedlist.js >>> >> >>> >> In that case, the Node.js people made it a pseudo-mixin rather than an >>> >> actual type for performance reasons - there's fewer object allocations >>> >> and they needed that. >>> >> >>> >> So I've considered the expando problem, and I disagree about it being >>> >> a problem at all. >>> >> >>> >> ----- >>> >> >>> >> Isiah Meadows >>> >> contact at isiahmeadows.com >>> >> www.isiahmeadows.com >>> >> >>> >> >>> >> On Mon, Jul 30, 2018 at 6:35 PM, Waldemar Horwat <waldemar at google.com> >>> >> wrote: >>> >> > On 07/29/2018 04:37 PM, Isiah Meadows wrote: >>> >> >> >>> >> >> BTW, I came up with an alternate proposal for privacy altogether: >>> >> >> https://github.com/tc39/proposal-class-fields/issues/115 >>> >> >> >>> >> >> TL;DR: private symbols that proxies can't see and that can't be >>> >> >> enumerated. >>> >> > >>> >> > >>> >> > Aside from syntax, the main semantic difference I see between this >>> >> > alternative and the main one is that this alternative defines >>> >> > private >>> >> > fields >>> >> > as expandos, creating opportunities for mischief by attaching them >>> >> > to >>> >> > unexpected objects. Aside from privacy, one of the things the >>> >> > private >>> >> > fields proposal gives you is consistency among multiple private >>> >> > fields >>> >> > on >>> >> > the same object. In the rare cases where you don't want that, you >>> >> > could >>> >> > use >>> >> > weak maps. >>> >> > >>> >> > Waldemar >>> >> _______________________________________________ >>> >> es-discuss mailing list >>> >> es-discuss at mozilla.org >>> >> https://mail.mozilla.org/listinfo/es-discuss
One last thing: how would you hope to deal with module-internal data stored
on arbitrary objects, using any means other than private symbols or something similar?
Isn't this precisely what WeakMaps are for? If the data is "module-internal", then the module needs to be the owner of the data store. If the data is about "arbitrary objects" (object from outside the module?) then those objects are the keys to the data store. If any object is thrown away, the associated data is no longer needed. If this doesn't fit the functionality of a WeakMap, I don't know what will.
Weak maps make sense when the weak map is the dictionary conceptually (think: caching).
Isn't that precisely what your question calls for? You're caching module-internal data about external objects.
Keep in mind, I'm specifically against the abuse of weak maps for private
state that's conceptually (in an abstract sense, not runtime) part of an object.
Likewise, I'm specifically against the abuse of objects to store state unrelated to the factory that created it. To me, that's as if I came to visit you and somehow you managed to hide some valuable info in my wallet without me noticing, and even if I completely dismantle my wallet, I wouldn't be able to find it. But somehow you can easily retrieve it the next time I come around. That's just conceptually weird.
> One last thing: how would you hope to deal with module-internal data stored on arbitrary objects, using any means other than private symbols or something similar? Isn't this precisely what WeakMaps are for? If the data is "module-internal", then the module needs to be the owner of the data store. If the data is about "arbitrary objects" (object from outside the module?) then those objects are the keys to the data store. If any object is thrown away, the associated data is no longer needed. If this doesn't fit the functionality of a WeakMap, I don't know what will. > Weak maps make sense when the weak map is the dictionary conceptually (think: caching). Isn't that precisely what your question calls for? You're caching module-internal data about external objects. > Keep in mind, I'm specifically *against* the abuse of weak maps for private state that's conceptually (in an abstract sense, not runtime) part of an object. Likewise, I'm specifically against the abuse of objects to store state unrelated to the factory that created it. To me, that's as if I came to visit you and somehow you managed to hide some valuable info in my wallet without me noticing, and even if I completely dismantle my wallet, I wouldn't be able to find it. But somehow you can easily retrieve it the next time I come around. That's just conceptually weird. On Mon, Jul 30, 2018 at 9:42 PM Isiah Meadows <isiahmeadows at gmail.com> wrote: > The reason private symbols are appropriate for Node's use case is > because it's conceptually a mixin, not a simple key/value map with > various utility functions (and weak map lookup is slower than property > access). JSDOM uses a similar utility [1] as a sort of mixin. > > Keep in mind, I'm specifically *against* the abuse of weak maps for > private state that's conceptually (in an abstract sense, not runtime) > part of an object. Weak maps make sense when the weak map is the > dictionary conceptually (think: caching). But if conceptually, the > object is the dictionary, putting it in a weak map is giving the > engine the wrong info - properties have inline caches and heavy > optimization, but you can't do the same for weak maps in the other > direction without literally implementing them as properties. (I would > *love* to be proven wrong here, BTW.) > > Let me draw a quick comparison: When do you use a map/set with string > keys, and when do you use an object instead? > > - Both are functionally equivalent, but engines use *very* different > algorithms for each one. > - I can almost guarantee you don't use maps when object properties work. > > One last thing: how would you hope to deal with module-internal data > stored on arbitrary objects, using any means other than private > symbols or something similar? To clarify, I'm talking of opaque object > structs [2], not simply classes. (BTW, that one is easier to manage as > a struct rather than a class, because of how many "methods" there are > operating on the state.) > > [1]: https://github.com/jsdom/js-symbol-tree > [2]: https://github.com/isiahmeadows/enigma/blob/master/src/parser.ts > > ----- > > Isiah Meadows > contact at isiahmeadows.com > www.isiahmeadows.com > > > On Mon, Jul 30, 2018 at 9:00 PM, Ranando King <kingmph at gmail.com> wrote: > > I meant to say if the object passed to the 3rd party function..... > > > > > > On Mon, Jul 30, 2018 at 7:59 PM Ranando King <kingmph at gmail.com> wrote: > >> > >> Just that use case alone is problematic. If the 3rd party function is > not > >> extensible, then the new private data should not be allowed. If the > library > >> cannot function without storing that data, then the function will have > no > >> choice but to fall back to WeakMaps which don't care if the key is not > >> extensible. So why not just stick with WeakMaps for that case? And if > that's > >> the case, then there would be little need for so open a means of > defining > >> private field names. The proposal I'm offering offers the room to > extend it > >> in the future to support everything else you might look for from your > >> private symbols idea.... unless you think I missed something. > >> > >> On Mon, Jul 30, 2018 at 7:26 PM Isiah Meadows <isiahmeadows at gmail.com> > >> wrote: > >>> > >>> That is one supported use case, yes. But that isn't the only use case > >>> this supports. It can still extend to traditional private class data, > >>> too. > >>> > >>> ----- > >>> > >>> Isiah Meadows > >>> contact at isiahmeadows.com > >>> www.isiahmeadows.com > >>> > >>> > >>> On Mon, Jul 30, 2018 at 8:04 PM, Ranando King <kingmph at gmail.com> > wrote: > >>> > So you're wanting the ability for a 3rd-party function to be able to > >>> > store > >>> > data private to that library on an object it didn't create, and that > >>> > only > >>> > that library can access? > >>> > > >>> > On Mon, Jul 30, 2018 at 6:36 PM Isiah Meadows < > isiahmeadows at gmail.com> > >>> > wrote: > >>> >> > >>> >> First, my private symbols are properly *private*. The only > >>> >> "unexpected" thing that could happen is making an object larger > >>> >> memory-wise, which engines already have to be equipped to handle now > >>> >> (libraries aren't always well-behaved, and like to occasionally add > >>> >> expando properties to builtins and DOM elements). About the only > thing > >>> >> most people would care about is in the debugger. > >>> >> > >>> >> Second, I had things like this in mind with supporting expando > >>> >> properties: > >>> >> > >>> >> > https://github.com/nodejs/node/blob/ae4fde8bc883686def5badfb324236320669e8f4/lib/internal/linkedlist.js > >>> >> > >>> >> In that case, the Node.js people made it a pseudo-mixin rather than > an > >>> >> actual type for performance reasons - there's fewer object > allocations > >>> >> and they needed that. > >>> >> > >>> >> So I've considered the expando problem, and I disagree about it > being > >>> >> a problem at all. > >>> >> > >>> >> ----- > >>> >> > >>> >> Isiah Meadows > >>> >> contact at isiahmeadows.com > >>> >> www.isiahmeadows.com > >>> >> > >>> >> > >>> >> On Mon, Jul 30, 2018 at 6:35 PM, Waldemar Horwat < > waldemar at google.com> > >>> >> wrote: > >>> >> > On 07/29/2018 04:37 PM, Isiah Meadows wrote: > >>> >> >> > >>> >> >> BTW, I came up with an alternate proposal for privacy altogether: > >>> >> >> https://github.com/tc39/proposal-class-fields/issues/115 > >>> >> >> > >>> >> >> TL;DR: private symbols that proxies can't see and that can't be > >>> >> >> enumerated. > >>> >> > > >>> >> > > >>> >> > Aside from syntax, the main semantic difference I see between this > >>> >> > alternative and the main one is that this alternative defines > >>> >> > private > >>> >> > fields > >>> >> > as expandos, creating opportunities for mischief by attaching them > >>> >> > to > >>> >> > unexpected objects. Aside from privacy, one of the things the > >>> >> > private > >>> >> > fields proposal gives you is consistency among multiple private > >>> >> > fields > >>> >> > on > >>> >> > the same object. In the rare cases where you don't want that, you > >>> >> > could > >>> >> > use > >>> >> > weak maps. > >>> >> > > >>> >> > Waldemar > >>> >> _______________________________________________ > >>> >> 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/20180731/6408bbb8/attachment-0001.html>
Isn't this precisely what WeakMaps are for? If the data is "module-internal", then the module needs to be the owner of the data store. If the data is about "arbitrary objects" (object from outside the module?) then those objects are the keys to the data store. If any object is thrown away, the associated data is no longer needed. If this doesn't fit the functionality of a WeakMap, I don't know what will.
Consider what people often use public symbols for now. For example, consider this library 1. In this case, they use a public symbol for their stuff in this file 2.
But here's the thing: that doesn't really need discoverable, and is a pure implementation detail. Wouldn't it make more sense for them to just use a private symbol instead? Because here, it's not a cache, but it's literally extra associated data in the object. And also, in that case, you want the engine to see it as a property, since it can employ relevant IC caching for it.
Isn't that precisely what your question calls for? You're caching module-internal data about external objects.
No, I'm not. I'm drawing a distinction between a pure many-to-one association (weak maps) and a "has a" relationship (private symbol properties). You could implement one in terms of the other, but these two types of relationships are completely different at a conceptual level and how you model them.
For js-symbol-tree, it's not simply associating a node to a value, but setting up the object so it has the data required for a doubly linked list tree node. Because this symbol is repeatedly accessed, it's not caching so much as it's adding data the object needs for it to do what it needs to do.
Another scenario is for JSDOM's Window
implementation, where they
have a few underscore-private variables like this 3. That particular
variable is used in several disparate parts throughout the code base
4, but is still conceptually a property. This is a case where a
private symbol property is appropriate.
Conversely in this JSDOM file 5, it's just associating data with an arbitrary object it happens to have, and so using the weak map makes perfect sense.
Likewise, I'm specifically against the abuse of objects to store state unrelated to the factory that created it. To me, that's as if I came to visit you and somehow you managed to hide some valuable info in my wallet without me noticing, and even if I completely dismantle my wallet, I wouldn't be able to find it. But somehow you can easily retrieve it the next time I come around. That's just conceptually weird.
All of the examples here I've presented are for scenarios where the state is related to the factory that created the objects. It's not directly related (and thus encapsulation is warranted), but it's still related, enough so that you usually see the state initialized within the creator's constructor call. It's about as related as the superclass is to a subclass of it.
BTW, you could make a similar argument against superclass private fields - it's like hiding valuable info in your wallet before you receive it for the first time, but even after dismantling it, you can't find any evidence of that valuable info.
Isiah Meadows contact at isiahmeadows.com, www.isiahmeadows.com
> Isn't this precisely what WeakMaps are for? If the data is > "module-internal", then the module needs to be the owner of the data store. > If the data is about "arbitrary objects" (object from outside the module?) > then those objects are the keys to the data store. If any object is thrown > away, the associated data is no longer needed. If this doesn't fit the > functionality of a WeakMap, I don't know what will. Consider what people often use public symbols for now. For example, consider this library [1]. In this case, they use a public symbol for their stuff in this file [2]. But here's the thing: that doesn't really need discoverable, and is a pure implementation detail. Wouldn't it make more sense for them to just use a private symbol instead? Because here, it's not a cache, but it's literally extra associated data in the object. And also, in that case, you *want* the engine to see it as a property, since it can employ relevant IC caching for it. > Isn't that precisely what your question calls for? You're caching > module-internal data about external objects. No, I'm not. I'm drawing a distinction between a pure many-to-one association (weak maps) and a "has a" relationship (private symbol properties). You *could* implement one in terms of the other, but these two types of relationships are *completely* different at a conceptual level and how you model them. For js-symbol-tree, it's not simply associating a node to a value, but setting up the object so it *has* the data required for a doubly linked list tree node. Because this symbol is repeatedly accessed, it's not caching so much as it's adding data the object needs for it to do what it needs to do. Another scenario is for JSDOM's `Window` implementation, where they have a few underscore-private variables like this [3]. That particular variable is used in several disparate parts throughout the code base [4], but is still conceptually a property. This is a case where a private symbol property is appropriate. Conversely in this JSDOM file [5], it's just associating data with an arbitrary object it happens to have, and so using the weak map makes perfect sense. > Likewise, I'm specifically against the abuse of objects to store state > unrelated to the factory that created it. To me, that's as if I came to > visit you and somehow you managed to hide some valuable info in my wallet > without me noticing, and even if I completely dismantle my wallet, I > wouldn't be able to find it. But somehow you can easily retrieve it the next > time I come around. That's just conceptually weird. All of the examples here I've presented are for scenarios where the state *is* related to the factory that created the objects. It's not *directly* related (and thus encapsulation is warranted), but it's still *related*, enough so that you usually see the state initialized within the creator's constructor call. It's about as related as the superclass is to a subclass of it. BTW, you could make a similar argument against superclass private fields - it's like hiding valuable info in your wallet before you receive it for the first time, but even after dismantling it, you can't find any evidence of that valuable info. [1]: https://github.com/jsdom/js-symbol-tree [2]: https://github.com/jsdom/js-symbol-tree/blob/master/lib/SymbolTree.js#L28 [3]: https://github.com/jsdom/jsdom/blob/23d67ebec901b3471b84e63f58a96b51a36f3671/lib/jsdom/browser/Window.js#L80 [4]: https://github.com/jsdom/jsdom/search?q=_globalProxy [5]: https://github.com/jsdom/jsdom/blob/ad0e551b1b633e07d11f98d7a30287491958def3/lib/jsdom/living/websockets/WebSocket-impl.js#L49 ----- Isiah Meadows contact at isiahmeadows.com www.isiahmeadows.com On Tue, Jul 31, 2018 at 1:55 AM, Ranando King <kingmph at gmail.com> wrote: >> One last thing: how would you hope to deal with module-internal data >> stored on arbitrary objects, using any means other than private symbols or >> something similar? > > Isn't this precisely what WeakMaps are for? If the data is > "module-internal", then the module needs to be the owner of the data store. > If the data is about "arbitrary objects" (object from outside the module?) > then those objects are the keys to the data store. If any object is thrown > away, the associated data is no longer needed. If this doesn't fit the > functionality of a WeakMap, I don't know what will. > >> Weak maps make sense when the weak map is the dictionary conceptually >> (think: caching). > > Isn't that precisely what your question calls for? You're caching > module-internal data about external objects. > >> Keep in mind, I'm specifically *against* the abuse of weak maps for >> private state that's conceptually (in an abstract sense, not runtime) part >> of an object. > > Likewise, I'm specifically against the abuse of objects to store state > unrelated to the factory that created it. To me, that's as if I came to > visit you and somehow you managed to hide some valuable info in my wallet > without me noticing, and even if I completely dismantle my wallet, I > wouldn't be able to find it. But somehow you can easily retrieve it the next > time I come around. That's just conceptually weird. > > On Mon, Jul 30, 2018 at 9:42 PM Isiah Meadows <isiahmeadows at gmail.com> > wrote: >> >> The reason private symbols are appropriate for Node's use case is >> because it's conceptually a mixin, not a simple key/value map with >> various utility functions (and weak map lookup is slower than property >> access). JSDOM uses a similar utility [1] as a sort of mixin. >> >> Keep in mind, I'm specifically *against* the abuse of weak maps for >> private state that's conceptually (in an abstract sense, not runtime) >> part of an object. Weak maps make sense when the weak map is the >> dictionary conceptually (think: caching). But if conceptually, the >> object is the dictionary, putting it in a weak map is giving the >> engine the wrong info - properties have inline caches and heavy >> optimization, but you can't do the same for weak maps in the other >> direction without literally implementing them as properties. (I would >> *love* to be proven wrong here, BTW.) >> >> Let me draw a quick comparison: When do you use a map/set with string >> keys, and when do you use an object instead? >> >> - Both are functionally equivalent, but engines use *very* different >> algorithms for each one. >> - I can almost guarantee you don't use maps when object properties work. >> >> One last thing: how would you hope to deal with module-internal data >> stored on arbitrary objects, using any means other than private >> symbols or something similar? To clarify, I'm talking of opaque object >> structs [2], not simply classes. (BTW, that one is easier to manage as >> a struct rather than a class, because of how many "methods" there are >> operating on the state.) >> >> [1]: https://github.com/jsdom/js-symbol-tree >> [2]: https://github.com/isiahmeadows/enigma/blob/master/src/parser.ts >> >> ----- >> >> Isiah Meadows >> contact at isiahmeadows.com >> www.isiahmeadows.com >> >> >> On Mon, Jul 30, 2018 at 9:00 PM, Ranando King <kingmph at gmail.com> wrote: >> > I meant to say if the object passed to the 3rd party function..... >> > >> > >> > On Mon, Jul 30, 2018 at 7:59 PM Ranando King <kingmph at gmail.com> wrote: >> >> >> >> Just that use case alone is problematic. If the 3rd party function is >> >> not >> >> extensible, then the new private data should not be allowed. If the >> >> library >> >> cannot function without storing that data, then the function will have >> >> no >> >> choice but to fall back to WeakMaps which don't care if the key is not >> >> extensible. So why not just stick with WeakMaps for that case? And if >> >> that's >> >> the case, then there would be little need for so open a means of >> >> defining >> >> private field names. The proposal I'm offering offers the room to >> >> extend it >> >> in the future to support everything else you might look for from your >> >> private symbols idea.... unless you think I missed something. >> >> >> >> On Mon, Jul 30, 2018 at 7:26 PM Isiah Meadows <isiahmeadows at gmail.com> >> >> wrote: >> >>> >> >>> That is one supported use case, yes. But that isn't the only use case >> >>> this supports. It can still extend to traditional private class data, >> >>> too. >> >>> >> >>> ----- >> >>> >> >>> Isiah Meadows >> >>> contact at isiahmeadows.com >> >>> www.isiahmeadows.com >> >>> >> >>> >> >>> On Mon, Jul 30, 2018 at 8:04 PM, Ranando King <kingmph at gmail.com> >> >>> wrote: >> >>> > So you're wanting the ability for a 3rd-party function to be able to >> >>> > store >> >>> > data private to that library on an object it didn't create, and that >> >>> > only >> >>> > that library can access? >> >>> > >> >>> > On Mon, Jul 30, 2018 at 6:36 PM Isiah Meadows >> >>> > <isiahmeadows at gmail.com> >> >>> > wrote: >> >>> >> >> >>> >> First, my private symbols are properly *private*. The only >> >>> >> "unexpected" thing that could happen is making an object larger >> >>> >> memory-wise, which engines already have to be equipped to handle >> >>> >> now >> >>> >> (libraries aren't always well-behaved, and like to occasionally add >> >>> >> expando properties to builtins and DOM elements). About the only >> >>> >> thing >> >>> >> most people would care about is in the debugger. >> >>> >> >> >>> >> Second, I had things like this in mind with supporting expando >> >>> >> properties: >> >>> >> >> >>> >> >> >>> >> https://github.com/nodejs/node/blob/ae4fde8bc883686def5badfb324236320669e8f4/lib/internal/linkedlist.js >> >>> >> >> >>> >> In that case, the Node.js people made it a pseudo-mixin rather than >> >>> >> an >> >>> >> actual type for performance reasons - there's fewer object >> >>> >> allocations >> >>> >> and they needed that. >> >>> >> >> >>> >> So I've considered the expando problem, and I disagree about it >> >>> >> being >> >>> >> a problem at all. >> >>> >> >> >>> >> ----- >> >>> >> >> >>> >> Isiah Meadows >> >>> >> contact at isiahmeadows.com >> >>> >> www.isiahmeadows.com >> >>> >> >> >>> >> >> >>> >> On Mon, Jul 30, 2018 at 6:35 PM, Waldemar Horwat >> >>> >> <waldemar at google.com> >> >>> >> wrote: >> >>> >> > On 07/29/2018 04:37 PM, Isiah Meadows wrote: >> >>> >> >> >> >>> >> >> BTW, I came up with an alternate proposal for privacy >> >>> >> >> altogether: >> >>> >> >> https://github.com/tc39/proposal-class-fields/issues/115 >> >>> >> >> >> >>> >> >> TL;DR: private symbols that proxies can't see and that can't be >> >>> >> >> enumerated. >> >>> >> > >> >>> >> > >> >>> >> > Aside from syntax, the main semantic difference I see between >> >>> >> > this >> >>> >> > alternative and the main one is that this alternative defines >> >>> >> > private >> >>> >> > fields >> >>> >> > as expandos, creating opportunities for mischief by attaching >> >>> >> > them >> >>> >> > to >> >>> >> > unexpected objects. Aside from privacy, one of the things the >> >>> >> > private >> >>> >> > fields proposal gives you is consistency among multiple private >> >>> >> > fields >> >>> >> > on >> >>> >> > the same object. In the rare cases where you don't want that, >> >>> >> > you >> >>> >> > could >> >>> >> > use >> >>> >> > weak maps. >> >>> >> > >> >>> >> > Waldemar >> >>> >> _______________________________________________ >> >>> >> es-discuss mailing list >> >>> >> es-discuss at mozilla.org >> >>> >> https://mail.mozilla.org/listinfo/es-discuss
Consider what people often use public symbols for now.
I know that I use them as fixed-value unique keys (module globals) for properties on objects that I export and don't want others to be aware of or able to touch.
For example, consider this library [1]. In this case, they use a public
symbol for their stuff in this file [2].
And as I said before, if someone passes a non-extensible object to this library, it breaks. Since any library can request any object be sealed or frozen, the implementation of this library is too fragile.
Because here, it's not a cache, but it's literally extra associated data
in the object. And also, in that case, you want the engine to see it as a property, since it can employ relevant IC caching for it.
Here's a parallel for you. Right now, Google has a database with information about me on it. I don't have access to that information (module privacy) and that information isn't stored anywhere on me or my devices (module locality). This is a proper conceptual model. The information Google is keeping about me is information they generated. Why should I have to pay to store their information? Thankfully I don't. However, this is precisely what you're claiming to be a good use case. You want module privacy without module locality. If I were to play at a Kevin Gibbons-like response, I'd say you've identified the common pattern, but that pattern itself is a bad use case, and the use of private symbols as you have defined them doesn't do anything to correct the technical issue. Since you cannot stick new properties onto a non-extensible object, even private symbols won't solve the problem with your use case.
No, I'm not. I'm drawing a distinction between a pure many-to-one association (weak maps) and a "has a" relationship (private symbol properties).
First, for any given property bag, the keys will need to be unique, but that doesn't force uniqueness onto the values. As such, even properties on an object provided by your "private Symbol" would still be many-1. When a 3rd party library wants to keep information associated with an arbitrary object, there are only 3 choices:
- try to store that information on the object
- this is what you're advocating, but it's not a good pattern. It's too fragile, being subject to break if the incoming object is not extensible.
- store the information as being associated to the object (WeakMap)
- this is the pattern that works in all cases (but the syntax is cumbersome and the implementation somewhat slow)
- return a wrapper containing the original object and the new information
(Proxy or custom wrapper)
- this is another possibility, but requires that any users accept and use the new Proxy or wrapper object in lieu of the original.
Another scenario is for JSDOM's
Window
implementation, where they have
a few underscore-private variables like this [3]. That particular variable is used in several disparate parts throughout the code base [4], but is still conceptually a property. This is a case where a private symbol property is appropriate.
It's not just "conceptually" a property. It's logically a property. Why? Because all the objects that it exists on were constructed somewhere within the JSDOM library. That's me putting my keys in my pocket. There's absolutely nothing wrong with that.
Conversely in this JSDOM file [5], it's just associating data with an arbitrary
object it happens to have, and so using the weak map makes perfect sense.
Conceptually speaking, this is the same scenario as SymbolTree. In both cases, the library is generating information associated with an object it doesn't own and didn't create.
BTW, you could make a similar argument against superclass private fields
- it's like hiding valuable info in your wallet before you receive it for the first time, but even after dismantling it, you can't find any evidence of that valuable info.
That dog don't hunt. The difference here is that in your use cases, library A is "sneakily" storing information on object B. In the case of superclass private fields, subclass B has "volunteered" to take on the information and functionality of class A. You've essentially compared apples and asteroids just because they both begin with "a".
> Consider what people often use public symbols for now. I know that I use them as fixed-value unique keys (module globals) for properties on objects that I export and don't want others to be aware of or able to touch. > For example, consider this library [1]. In this case, they use a public symbol for their stuff in this file [2]. And as I said before, if someone passes a non-extensible object to this library, it breaks. Since any library can request any object be sealed or frozen, the implementation of this library is too fragile. > Because here, it's not a cache, but it's literally extra associated data in the object. And also, in that case, you *want* the engine to see it as a property, since it can employ relevant IC caching for it. Here's a parallel for you. Right now, Google has a database with information about me on it. I don't have access to that information (module privacy) and that information isn't stored anywhere on me or my devices (module locality). This is a proper conceptual model. The information Google is keeping about me is information they generated. Why should I have to pay to store their information? Thankfully I don't. However, this is precisely what you're claiming to be a good use case. You want module privacy without module locality. If I were to play at a Kevin Gibbons-like response, I'd say you've identified the common pattern, but that pattern itself is a bad use case, and the use of private symbols as you have defined them doesn't do anything to correct the technical issue. Since you cannot stick new properties onto a non-extensible object, even private symbols won't solve the problem with your use case. > No, I'm not. I'm drawing a distinction between a pure many-to-one association (weak maps) and a "has a" relationship (private symbol properties). First, for any given property bag, the keys will need to be unique, but that doesn't force uniqueness onto the values. As such, even properties on an object provided by your "private Symbol" would still be many-1. When a 3rd party library wants to keep information associated with an arbitrary object, there are only 3 choices: * try to store that information on the object * this is what you're advocating, but it's not a good pattern. It's too fragile, being subject to break if the incoming object is not extensible. * store the information as being associated to the object (WeakMap) * this is the pattern that works in all cases (but the syntax is cumbersome and the implementation somewhat slow) * return a wrapper containing the original object and the new information (Proxy or custom wrapper) * this is another possibility, but requires that any users accept and use the new Proxy or wrapper object in lieu of the original. > Another scenario is for JSDOM's `Window` implementation, where they have a few underscore-private variables like this [3]. That particular variable is used in several disparate parts throughout the code base [4], but is still conceptually a property. This is a case where a private symbol property is appropriate. It's not just "conceptually" a property. It's logically a property. Why? Because all the objects that it exists on were constructed somewhere within the JSDOM library. That's me putting _my_ keys in _my_ pocket. There's absolutely nothing wrong with that. > Conversely in this JSDOM file [5], it's just associating data with an arbitrary object it happens to have, and so using the weak map makes perfect sense. Conceptually speaking, this is the same scenario as SymbolTree. In both cases, the library is generating information associated with an object it doesn't own and didn't create. > BTW, you could make a similar argument against superclass private fields - it's like hiding valuable info in your wallet before you receive it for the first time, but even after dismantling it, you can't find any evidence of that valuable info. That dog don't hunt. The difference here is that in your use cases, library A is "sneakily" storing information on object B. In the case of superclass private fields, subclass B has "volunteered" to take on the information and functionality of class A. You've essentially compared apples and asteroids just because they both begin with "a". On Tue, Jul 31, 2018 at 2:15 AM Isiah Meadows <isiahmeadows at gmail.com> wrote: > > Isn't this precisely what WeakMaps are for? If the data is > > "module-internal", then the module needs to be the owner of the data > store. > > If the data is about "arbitrary objects" (object from outside the > module?) > > then those objects are the keys to the data store. If any object is > thrown > > away, the associated data is no longer needed. If this doesn't fit the > > functionality of a WeakMap, I don't know what will. > > Consider what people often use public symbols for now. For example, > consider this library [1]. In this case, they use a public symbol for > their stuff in this file [2]. > > But here's the thing: that doesn't really need discoverable, and is a > pure implementation detail. Wouldn't it make more sense for them to > just use a private symbol instead? Because here, it's not a cache, but > it's literally extra associated data in the object. And also, in that > case, you *want* the engine to see it as a property, since it can > employ relevant IC caching for it. > > > Isn't that precisely what your question calls for? You're caching > > module-internal data about external objects. > > No, I'm not. I'm drawing a distinction between a pure many-to-one > association (weak maps) and a "has a" relationship (private symbol > properties). You *could* implement one in terms of the other, but > these two types of relationships are *completely* different at a > conceptual level and how you model them. > > For js-symbol-tree, it's not simply associating a node to a value, but > setting up the object so it *has* the data required for a doubly > linked list tree node. Because this symbol is repeatedly accessed, > it's not caching so much as it's adding data the object needs for it > to do what it needs to do. > > Another scenario is for JSDOM's `Window` implementation, where they > have a few underscore-private variables like this [3]. That particular > variable is used in several disparate parts throughout the code base > [4], but is still conceptually a property. This is a case where a > private symbol property is appropriate. > > Conversely in this JSDOM file [5], it's just associating data with an > arbitrary object it happens to have, and so using the weak map makes > perfect sense. > > > Likewise, I'm specifically against the abuse of objects to store state > > unrelated to the factory that created it. To me, that's as if I came to > > visit you and somehow you managed to hide some valuable info in my wallet > > without me noticing, and even if I completely dismantle my wallet, I > > wouldn't be able to find it. But somehow you can easily retrieve it the > next > > time I come around. That's just conceptually weird. > > All of the examples here I've presented are for scenarios where the > state *is* related to the factory that created the objects. It's not > *directly* related (and thus encapsulation is warranted), but it's > still *related*, enough so that you usually see the state initialized > within the creator's constructor call. It's about as related as the > superclass is to a subclass of it. > > BTW, you could make a similar argument against superclass private > fields - it's like hiding valuable info in your wallet before you > receive it for the first time, but even after dismantling it, you > can't find any evidence of that valuable info. > > [1]: https://github.com/jsdom/js-symbol-tree > [2]: > https://github.com/jsdom/js-symbol-tree/blob/master/lib/SymbolTree.js#L28 > [3]: > https://github.com/jsdom/jsdom/blob/23d67ebec901b3471b84e63f58a96b51a36f3671/lib/jsdom/browser/Window.js#L80 > [4]: https://github.com/jsdom/jsdom/search?q=_globalProxy > [5]: > https://github.com/jsdom/jsdom/blob/ad0e551b1b633e07d11f98d7a30287491958def3/lib/jsdom/living/websockets/WebSocket-impl.js#L49 > > ----- > > Isiah Meadows > contact at isiahmeadows.com > www.isiahmeadows.com > > > On Tue, Jul 31, 2018 at 1:55 AM, Ranando King <kingmph at gmail.com> wrote: > >> One last thing: how would you hope to deal with module-internal data > >> stored on arbitrary objects, using any means other than private symbols > or > >> something similar? > > > > Isn't this precisely what WeakMaps are for? If the data is > > "module-internal", then the module needs to be the owner of the data > store. > > If the data is about "arbitrary objects" (object from outside the > module?) > > then those objects are the keys to the data store. If any object is > thrown > > away, the associated data is no longer needed. If this doesn't fit the > > functionality of a WeakMap, I don't know what will. > > > >> Weak maps make sense when the weak map is the dictionary conceptually > >> (think: caching). > > > > Isn't that precisely what your question calls for? You're caching > > module-internal data about external objects. > > > >> Keep in mind, I'm specifically *against* the abuse of weak maps for > >> private state that's conceptually (in an abstract sense, not runtime) > part > >> of an object. > > > > Likewise, I'm specifically against the abuse of objects to store state > > unrelated to the factory that created it. To me, that's as if I came to > > visit you and somehow you managed to hide some valuable info in my wallet > > without me noticing, and even if I completely dismantle my wallet, I > > wouldn't be able to find it. But somehow you can easily retrieve it the > next > > time I come around. That's just conceptually weird. > > > > On Mon, Jul 30, 2018 at 9:42 PM Isiah Meadows <isiahmeadows at gmail.com> > > wrote: > >> > >> The reason private symbols are appropriate for Node's use case is > >> because it's conceptually a mixin, not a simple key/value map with > >> various utility functions (and weak map lookup is slower than property > >> access). JSDOM uses a similar utility [1] as a sort of mixin. > >> > >> Keep in mind, I'm specifically *against* the abuse of weak maps for > >> private state that's conceptually (in an abstract sense, not runtime) > >> part of an object. Weak maps make sense when the weak map is the > >> dictionary conceptually (think: caching). But if conceptually, the > >> object is the dictionary, putting it in a weak map is giving the > >> engine the wrong info - properties have inline caches and heavy > >> optimization, but you can't do the same for weak maps in the other > >> direction without literally implementing them as properties. (I would > >> *love* to be proven wrong here, BTW.) > >> > >> Let me draw a quick comparison: When do you use a map/set with string > >> keys, and when do you use an object instead? > >> > >> - Both are functionally equivalent, but engines use *very* different > >> algorithms for each one. > >> - I can almost guarantee you don't use maps when object properties work. > >> > >> One last thing: how would you hope to deal with module-internal data > >> stored on arbitrary objects, using any means other than private > >> symbols or something similar? To clarify, I'm talking of opaque object > >> structs [2], not simply classes. (BTW, that one is easier to manage as > >> a struct rather than a class, because of how many "methods" there are > >> operating on the state.) > >> > >> [1]: https://github.com/jsdom/js-symbol-tree > >> [2]: https://github.com/isiahmeadows/enigma/blob/master/src/parser.ts > >> > >> ----- > >> > >> Isiah Meadows > >> contact at isiahmeadows.com > >> www.isiahmeadows.com > >> > >> > >> On Mon, Jul 30, 2018 at 9:00 PM, Ranando King <kingmph at gmail.com> > wrote: > >> > I meant to say if the object passed to the 3rd party function..... > >> > > >> > > >> > On Mon, Jul 30, 2018 at 7:59 PM Ranando King <kingmph at gmail.com> > wrote: > >> >> > >> >> Just that use case alone is problematic. If the 3rd party function is > >> >> not > >> >> extensible, then the new private data should not be allowed. If the > >> >> library > >> >> cannot function without storing that data, then the function will > have > >> >> no > >> >> choice but to fall back to WeakMaps which don't care if the key is > not > >> >> extensible. So why not just stick with WeakMaps for that case? And if > >> >> that's > >> >> the case, then there would be little need for so open a means of > >> >> defining > >> >> private field names. The proposal I'm offering offers the room to > >> >> extend it > >> >> in the future to support everything else you might look for from your > >> >> private symbols idea.... unless you think I missed something. > >> >> > >> >> On Mon, Jul 30, 2018 at 7:26 PM Isiah Meadows < > isiahmeadows at gmail.com> > >> >> wrote: > >> >>> > >> >>> That is one supported use case, yes. But that isn't the only use > case > >> >>> this supports. It can still extend to traditional private class > data, > >> >>> too. > >> >>> > >> >>> ----- > >> >>> > >> >>> Isiah Meadows > >> >>> contact at isiahmeadows.com > >> >>> www.isiahmeadows.com > >> >>> > >> >>> > >> >>> On Mon, Jul 30, 2018 at 8:04 PM, Ranando King <kingmph at gmail.com> > >> >>> wrote: > >> >>> > So you're wanting the ability for a 3rd-party function to be able > to > >> >>> > store > >> >>> > data private to that library on an object it didn't create, and > that > >> >>> > only > >> >>> > that library can access? > >> >>> > > >> >>> > On Mon, Jul 30, 2018 at 6:36 PM Isiah Meadows > >> >>> > <isiahmeadows at gmail.com> > >> >>> > wrote: > >> >>> >> > >> >>> >> First, my private symbols are properly *private*. The only > >> >>> >> "unexpected" thing that could happen is making an object larger > >> >>> >> memory-wise, which engines already have to be equipped to handle > >> >>> >> now > >> >>> >> (libraries aren't always well-behaved, and like to occasionally > add > >> >>> >> expando properties to builtins and DOM elements). About the only > >> >>> >> thing > >> >>> >> most people would care about is in the debugger. > >> >>> >> > >> >>> >> Second, I had things like this in mind with supporting expando > >> >>> >> properties: > >> >>> >> > >> >>> >> > >> >>> >> > https://github.com/nodejs/node/blob/ae4fde8bc883686def5badfb324236320669e8f4/lib/internal/linkedlist.js > >> >>> >> > >> >>> >> In that case, the Node.js people made it a pseudo-mixin rather > than > >> >>> >> an > >> >>> >> actual type for performance reasons - there's fewer object > >> >>> >> allocations > >> >>> >> and they needed that. > >> >>> >> > >> >>> >> So I've considered the expando problem, and I disagree about it > >> >>> >> being > >> >>> >> a problem at all. > >> >>> >> > >> >>> >> ----- > >> >>> >> > >> >>> >> Isiah Meadows > >> >>> >> contact at isiahmeadows.com > >> >>> >> www.isiahmeadows.com > >> >>> >> > >> >>> >> > >> >>> >> On Mon, Jul 30, 2018 at 6:35 PM, Waldemar Horwat > >> >>> >> <waldemar at google.com> > >> >>> >> wrote: > >> >>> >> > On 07/29/2018 04:37 PM, Isiah Meadows wrote: > >> >>> >> >> > >> >>> >> >> BTW, I came up with an alternate proposal for privacy > >> >>> >> >> altogether: > >> >>> >> >> https://github.com/tc39/proposal-class-fields/issues/115 > >> >>> >> >> > >> >>> >> >> TL;DR: private symbols that proxies can't see and that can't > be > >> >>> >> >> enumerated. > >> >>> >> > > >> >>> >> > > >> >>> >> > Aside from syntax, the main semantic difference I see between > >> >>> >> > this > >> >>> >> > alternative and the main one is that this alternative defines > >> >>> >> > private > >> >>> >> > fields > >> >>> >> > as expandos, creating opportunities for mischief by attaching > >> >>> >> > them > >> >>> >> > to > >> >>> >> > unexpected objects. Aside from privacy, one of the things the > >> >>> >> > private > >> >>> >> > fields proposal gives you is consistency among multiple private > >> >>> >> > fields > >> >>> >> > on > >> >>> >> > the same object. In the rare cases where you don't want that, > >> >>> >> > you > >> >>> >> > could > >> >>> >> > use > >> >>> >> > weak maps. > >> >>> >> > > >> >>> >> > Waldemar > >> >>> >> _______________________________________________ > >> >>> >> 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/20180731/7cbda322/attachment-0001.html>
Should a sealed/frozen object be privately extensible? I don't actually know, but interesting point.
Should a sealed/frozen object be privately extensible? I don't actually know, but interesting point. On Tuesday, July 31, 2018, Ranando King <kingmph at gmail.com> wrote: > > Consider what people often use public symbols for now. > > I know that I use them as fixed-value unique keys (module globals) for > properties on objects that I export and don't want others to be aware of or > able to touch. > > > For example, consider this library [1]. In this case, they use a public > symbol for their stuff in this file [2]. > > And as I said before, if someone passes a non-extensible object to this > library, it breaks. Since any library can request any object be sealed or > frozen, the implementation of this library is too fragile. > > > Because here, it's not a cache, but it's literally extra associated > data in the object. And also, in that case, you *want* the engine to see > it as a property, since it can employ relevant IC caching for it. > > Here's a parallel for you. Right now, Google has a database with > information about me on it. I don't have access to that information (module > privacy) and that information isn't stored anywhere on me or my devices > (module locality). This is a proper conceptual model. The information > Google is keeping about me is information they generated. Why should I have > to pay to store their information? Thankfully I don't. However, this is > precisely what you're claiming to be a good use case. You want module > privacy without module locality. If I were to play at a Kevin Gibbons-like > response, I'd say you've identified the common pattern, but that pattern > itself is a bad use case, and the use of private symbols as you have > defined them doesn't do anything to correct the technical issue. Since you > cannot stick new properties onto a non-extensible object, even private > symbols won't solve the problem with your use case. > > > No, I'm not. I'm drawing a distinction between a pure many-to-one association > (weak maps) and a "has a" relationship (private symbol properties). > > First, for any given property bag, the keys will need to be unique, but > that doesn't force uniqueness onto the values. As such, even properties on > an object provided by your "private Symbol" would still be many-1. When a > 3rd party library wants to keep information associated with an arbitrary > object, there are only 3 choices: > * try to store that information on the object > * this is what you're advocating, but it's not a good pattern. It's too > fragile, being subject to break if the incoming object is not extensible. > * store the information as being associated to the object (WeakMap) > * this is the pattern that works in all cases (but the syntax is > cumbersome and the implementation somewhat slow) > * return a wrapper containing the original object and the new information > (Proxy or custom wrapper) > * this is another possibility, but requires that any users accept and > use the new Proxy or wrapper object in lieu of the original. > > > Another scenario is for JSDOM's `Window` implementation, where they have > a few underscore-private variables like this [3]. That particular variable > is used in several disparate parts throughout the code base [4], but is > still conceptually a property. This is a case where a private symbol > property is appropriate. > > It's not just "conceptually" a property. It's logically a property. Why? > Because all the objects that it exists on were constructed somewhere within > the JSDOM library. That's me putting _my_ keys in _my_ pocket. There's > absolutely nothing wrong with that. > > > Conversely in this JSDOM file [5], it's just associating data with an arbitrary > object it happens to have, and so using the weak map makes perfect sense. > > Conceptually speaking, this is the same scenario as SymbolTree. In both > cases, the library is generating information associated with an object it > doesn't own and didn't create. > > > BTW, you could make a similar argument against superclass private fields > - it's like hiding valuable info in your wallet before you receive it for > the first time, but even after dismantling it, you can't find any > evidence of that valuable info. > > That dog don't hunt. The difference here is that in your use cases, > library A is "sneakily" storing information on object B. In the case of > superclass private fields, subclass B has "volunteered" to take on the > information and functionality of class A. You've essentially compared > apples and asteroids just because they both begin with "a". > > On Tue, Jul 31, 2018 at 2:15 AM Isiah Meadows <isiahmeadows at gmail.com> > wrote: > >> > Isn't this precisely what WeakMaps are for? If the data is >> > "module-internal", then the module needs to be the owner of the data >> store. >> > If the data is about "arbitrary objects" (object from outside the >> module?) >> > then those objects are the keys to the data store. If any object is >> thrown >> > away, the associated data is no longer needed. If this doesn't fit the >> > functionality of a WeakMap, I don't know what will. >> >> Consider what people often use public symbols for now. For example, >> consider this library [1]. In this case, they use a public symbol for >> their stuff in this file [2]. >> >> But here's the thing: that doesn't really need discoverable, and is a >> pure implementation detail. Wouldn't it make more sense for them to >> just use a private symbol instead? Because here, it's not a cache, but >> it's literally extra associated data in the object. And also, in that >> case, you *want* the engine to see it as a property, since it can >> employ relevant IC caching for it. >> >> > Isn't that precisely what your question calls for? You're caching >> > module-internal data about external objects. >> >> No, I'm not. I'm drawing a distinction between a pure many-to-one >> association (weak maps) and a "has a" relationship (private symbol >> properties). You *could* implement one in terms of the other, but >> these two types of relationships are *completely* different at a >> conceptual level and how you model them. >> >> For js-symbol-tree, it's not simply associating a node to a value, but >> setting up the object so it *has* the data required for a doubly >> linked list tree node. Because this symbol is repeatedly accessed, >> it's not caching so much as it's adding data the object needs for it >> to do what it needs to do. >> >> Another scenario is for JSDOM's `Window` implementation, where they >> have a few underscore-private variables like this [3]. That particular >> variable is used in several disparate parts throughout the code base >> [4], but is still conceptually a property. This is a case where a >> private symbol property is appropriate. >> >> Conversely in this JSDOM file [5], it's just associating data with an >> arbitrary object it happens to have, and so using the weak map makes >> perfect sense. >> >> > Likewise, I'm specifically against the abuse of objects to store state >> > unrelated to the factory that created it. To me, that's as if I came to >> > visit you and somehow you managed to hide some valuable info in my >> wallet >> > without me noticing, and even if I completely dismantle my wallet, I >> > wouldn't be able to find it. But somehow you can easily retrieve it the >> next >> > time I come around. That's just conceptually weird. >> >> All of the examples here I've presented are for scenarios where the >> state *is* related to the factory that created the objects. It's not >> *directly* related (and thus encapsulation is warranted), but it's >> still *related*, enough so that you usually see the state initialized >> within the creator's constructor call. It's about as related as the >> superclass is to a subclass of it. >> >> BTW, you could make a similar argument against superclass private >> fields - it's like hiding valuable info in your wallet before you >> receive it for the first time, but even after dismantling it, you >> can't find any evidence of that valuable info. >> >> [1]: https://github.com/jsdom/js-symbol-tree >> [2]: https://github.com/jsdom/js-symbol-tree/blob/master/lib/ >> SymbolTree.js#L28 >> [3]: https://github.com/jsdom/jsdom/blob/23d67ebec901b3471b84e63f58a96b >> 51a36f3671/lib/jsdom/browser/Window.js#L80 >> [4]: https://github.com/jsdom/jsdom/search?q=_globalProxy >> [5]: https://github.com/jsdom/jsdom/blob/ad0e551b1b633e07d11f98d7a30287 >> 491958def3/lib/jsdom/living/websockets/WebSocket-impl.js#L49 >> >> ----- >> >> Isiah Meadows >> contact at isiahmeadows.com >> www.isiahmeadows.com >> >> >> On Tue, Jul 31, 2018 at 1:55 AM, Ranando King <kingmph at gmail.com> wrote: >> >> One last thing: how would you hope to deal with module-internal data >> >> stored on arbitrary objects, using any means other than private >> symbols or >> >> something similar? >> > >> > Isn't this precisely what WeakMaps are for? If the data is >> > "module-internal", then the module needs to be the owner of the data >> store. >> > If the data is about "arbitrary objects" (object from outside the >> module?) >> > then those objects are the keys to the data store. If any object is >> thrown >> > away, the associated data is no longer needed. If this doesn't fit the >> > functionality of a WeakMap, I don't know what will. >> > >> >> Weak maps make sense when the weak map is the dictionary conceptually >> >> (think: caching). >> > >> > Isn't that precisely what your question calls for? You're caching >> > module-internal data about external objects. >> > >> >> Keep in mind, I'm specifically *against* the abuse of weak maps for >> >> private state that's conceptually (in an abstract sense, not runtime) >> part >> >> of an object. >> > >> > Likewise, I'm specifically against the abuse of objects to store state >> > unrelated to the factory that created it. To me, that's as if I came to >> > visit you and somehow you managed to hide some valuable info in my >> wallet >> > without me noticing, and even if I completely dismantle my wallet, I >> > wouldn't be able to find it. But somehow you can easily retrieve it the >> next >> > time I come around. That's just conceptually weird. >> > >> > On Mon, Jul 30, 2018 at 9:42 PM Isiah Meadows <isiahmeadows at gmail.com> >> > wrote: >> >> >> >> The reason private symbols are appropriate for Node's use case is >> >> because it's conceptually a mixin, not a simple key/value map with >> >> various utility functions (and weak map lookup is slower than property >> >> access). JSDOM uses a similar utility [1] as a sort of mixin. >> >> >> >> Keep in mind, I'm specifically *against* the abuse of weak maps for >> >> private state that's conceptually (in an abstract sense, not runtime) >> >> part of an object. Weak maps make sense when the weak map is the >> >> dictionary conceptually (think: caching). But if conceptually, the >> >> object is the dictionary, putting it in a weak map is giving the >> >> engine the wrong info - properties have inline caches and heavy >> >> optimization, but you can't do the same for weak maps in the other >> >> direction without literally implementing them as properties. (I would >> >> *love* to be proven wrong here, BTW.) >> >> >> >> Let me draw a quick comparison: When do you use a map/set with string >> >> keys, and when do you use an object instead? >> >> >> >> - Both are functionally equivalent, but engines use *very* different >> >> algorithms for each one. >> >> - I can almost guarantee you don't use maps when object properties >> work. >> >> >> >> One last thing: how would you hope to deal with module-internal data >> >> stored on arbitrary objects, using any means other than private >> >> symbols or something similar? To clarify, I'm talking of opaque object >> >> structs [2], not simply classes. (BTW, that one is easier to manage as >> >> a struct rather than a class, because of how many "methods" there are >> >> operating on the state.) >> >> >> >> [1]: https://github.com/jsdom/js-symbol-tree >> >> [2]: https://github.com/isiahmeadows/enigma/blob/master/src/parser.ts >> >> >> >> ----- >> >> >> >> Isiah Meadows >> >> contact at isiahmeadows.com >> >> www.isiahmeadows.com >> >> >> >> >> >> On Mon, Jul 30, 2018 at 9:00 PM, Ranando King <kingmph at gmail.com> >> wrote: >> >> > I meant to say if the object passed to the 3rd party function..... >> >> > >> >> > >> >> > On Mon, Jul 30, 2018 at 7:59 PM Ranando King <kingmph at gmail.com> >> wrote: >> >> >> >> >> >> Just that use case alone is problematic. If the 3rd party function >> is >> >> >> not >> >> >> extensible, then the new private data should not be allowed. If the >> >> >> library >> >> >> cannot function without storing that data, then the function will >> have >> >> >> no >> >> >> choice but to fall back to WeakMaps which don't care if the key is >> not >> >> >> extensible. So why not just stick with WeakMaps for that case? And >> if >> >> >> that's >> >> >> the case, then there would be little need for so open a means of >> >> >> defining >> >> >> private field names. The proposal I'm offering offers the room to >> >> >> extend it >> >> >> in the future to support everything else you might look for from >> your >> >> >> private symbols idea.... unless you think I missed something. >> >> >> >> >> >> On Mon, Jul 30, 2018 at 7:26 PM Isiah Meadows < >> isiahmeadows at gmail.com> >> >> >> wrote: >> >> >>> >> >> >>> That is one supported use case, yes. But that isn't the only use >> case >> >> >>> this supports. It can still extend to traditional private class >> data, >> >> >>> too. >> >> >>> >> >> >>> ----- >> >> >>> >> >> >>> Isiah Meadows >> >> >>> contact at isiahmeadows.com >> >> >>> www.isiahmeadows.com >> >> >>> >> >> >>> >> >> >>> On Mon, Jul 30, 2018 at 8:04 PM, Ranando King <kingmph at gmail.com> >> >> >>> wrote: >> >> >>> > So you're wanting the ability for a 3rd-party function to be >> able to >> >> >>> > store >> >> >>> > data private to that library on an object it didn't create, and >> that >> >> >>> > only >> >> >>> > that library can access? >> >> >>> > >> >> >>> > On Mon, Jul 30, 2018 at 6:36 PM Isiah Meadows >> >> >>> > <isiahmeadows at gmail.com> >> >> >>> > wrote: >> >> >>> >> >> >> >>> >> First, my private symbols are properly *private*. The only >> >> >>> >> "unexpected" thing that could happen is making an object larger >> >> >>> >> memory-wise, which engines already have to be equipped to handle >> >> >>> >> now >> >> >>> >> (libraries aren't always well-behaved, and like to occasionally >> add >> >> >>> >> expando properties to builtins and DOM elements). About the only >> >> >>> >> thing >> >> >>> >> most people would care about is in the debugger. >> >> >>> >> >> >> >>> >> Second, I had things like this in mind with supporting expando >> >> >>> >> properties: >> >> >>> >> >> >> >>> >> >> >> >>> >> https://github.com/nodejs/node/blob/ >> ae4fde8bc883686def5badfb324236320669e8f4/lib/internal/linkedlist.js >> >> >>> >> >> >> >>> >> In that case, the Node.js people made it a pseudo-mixin rather >> than >> >> >>> >> an >> >> >>> >> actual type for performance reasons - there's fewer object >> >> >>> >> allocations >> >> >>> >> and they needed that. >> >> >>> >> >> >> >>> >> So I've considered the expando problem, and I disagree about it >> >> >>> >> being >> >> >>> >> a problem at all. >> >> >>> >> >> >> >>> >> ----- >> >> >>> >> >> >> >>> >> Isiah Meadows >> >> >>> >> contact at isiahmeadows.com >> >> >>> >> www.isiahmeadows.com >> >> >>> >> >> >> >>> >> >> >> >>> >> On Mon, Jul 30, 2018 at 6:35 PM, Waldemar Horwat >> >> >>> >> <waldemar at google.com> >> >> >>> >> wrote: >> >> >>> >> > On 07/29/2018 04:37 PM, Isiah Meadows wrote: >> >> >>> >> >> >> >> >>> >> >> BTW, I came up with an alternate proposal for privacy >> >> >>> >> >> altogether: >> >> >>> >> >> https://github.com/tc39/proposal-class-fields/issues/115 >> >> >>> >> >> >> >> >>> >> >> TL;DR: private symbols that proxies can't see and that can't >> be >> >> >>> >> >> enumerated. >> >> >>> >> > >> >> >>> >> > >> >> >>> >> > Aside from syntax, the main semantic difference I see between >> >> >>> >> > this >> >> >>> >> > alternative and the main one is that this alternative defines >> >> >>> >> > private >> >> >>> >> > fields >> >> >>> >> > as expandos, creating opportunities for mischief by attaching >> >> >>> >> > them >> >> >>> >> > to >> >> >>> >> > unexpected objects. Aside from privacy, one of the things the >> >> >>> >> > private >> >> >>> >> > fields proposal gives you is consistency among multiple >> private >> >> >>> >> > fields >> >> >>> >> > on >> >> >>> >> > the same object. In the rare cases where you don't want that, >> >> >>> >> > you >> >> >>> >> > could >> >> >>> >> > use >> >> >>> >> > weak maps. >> >> >>> >> > >> >> >>> >> > Waldemar >> >> >>> >> _______________________________________________ >> >> >>> >> 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/20180731/b2aa8da6/attachment-0001.html>
I'd say you've identified the common pattern, but that pattern itself is a bad use case, and the use of private symbols as you have defined them doesn't do anything to correct the technical issue.
I think there’s been a misunderstanding. Everybody agrees that that’s a bad pattern. It’s not what the point of private symbols would be. It’s not a target use case.
Since you cannot stick new properties onto a non-extensible object, even private symbols won't solve the problem with your use case.
That appending private symbols to external objects which are frozen wouldn’t work doesn’t matter precisely because it’s not a target use case. That it doesn’t work reliably might even be considered a positive, since it discourages something we all seem to agree is not good practice.
It’s also not related to private symbols; this is already how properties work, regardless of what kind of key they have.
The difference here is that in your use cases, library A is "sneakily" storing information on object B.
What use case are you referring to here? I can’t find any example in the previous posts that matches these descriptions. As Isiah said, “all of the examples here I've presented are for scenarios where the state is related to the factory that created the objects.” The same is true of my examples. Everybody’s on the same page regarding not wanting to add properties to objects their own libraries do not create.
> I'd say you've identified the common pattern, but that pattern itself is a bad use case, and the use of private symbols as you have defined them doesn't do anything to correct the technical issue. I think there’s been a misunderstanding. Everybody agrees that that’s a bad pattern. It’s not what the point of private symbols would be. It’s not a target use case. > Since you cannot stick new properties onto a non-extensible object, even private symbols won't solve the problem with your use case. That appending private symbols to external objects which are frozen wouldn’t work doesn’t matter precisely because it’s not a target use case. That it doesn’t work reliably might even be considered a positive, since it discourages something we all seem to agree is not good practice. It’s also not related to private symbols; this is already how properties work, regardless of what kind of key they have. > The difference here is that in your use cases, library A is "sneakily" storing information on object B. What use case are you referring to here? I can’t find any example in the previous posts that matches these descriptions. As Isiah said, “all of the examples here I've presented are for scenarios where the state is related to the factory that created the objects.” The same is true of my examples. Everybody’s on the same page regarding not wanting to add properties to objects their own libraries do not create. -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180731/2255a08f/attachment.html>
What use case are you referring to here?
In the case of SymbolTree, the objects in use are external.
I think there’s been a misunderstanding. Everybody agrees that that’s a
bad pattern. It’s not what the point of private symbols would be. It’s not a target use case.
That certainly puts my mind at ease.
As Isiah said, “all of the examples here I've presented are for scenarios
where the state is related to the factory that created the objects.”
If the factory that creates the objects is the also the only thing trying to store private information on those objects, then I understand you're only looking for per-instance module-private data, possibly with the ability to use common private names. If that's the case, then it really is just 2 simple extensions of my proposal:
- allow a Symbol when used as a private or protected property name to persist as the private Symbol name for the private instance field on each object for which it is used.
- create an additional privilege level (internal) that places the new field's name in the [[DeclarationInfo]] of the function containing the declaration.
The effect of using these 2 features together is that anything within the same function as the declared Symbol will gain access to the internal field of all objects using that Symbol as a field name.
> What use case are you referring to here? In the case of SymbolTree, the objects in use are external. > I think there’s been a misunderstanding. Everybody agrees that that’s a bad pattern. It’s not what the point of private symbols would be. It’s not a target use case. That certainly puts my mind at ease. > As Isiah said, “all of the examples here I've presented are for scenarios where the state is related to the factory that created the objects.” If the factory that creates the objects is the also the only thing trying to store private information on those objects, then I understand you're only looking for per-instance module-private data, possibly with the ability to use common private names. If that's the case, then it really is just 2 simple extensions of my proposal: * allow a Symbol when used as a private or protected property name to persist as the private Symbol name for the private instance field on each object for which it is used. * create an additional privilege level (internal) that places the new field's name in the [[DeclarationInfo]] of the function containing the declaration. The effect of using these 2 features together is that anything within the same function as the declared Symbol will gain access to the internal field of all objects using that Symbol as a field name. On Tue, Jul 31, 2018 at 1:36 PM Darien Valentine <valentinium at gmail.com> wrote: > > I'd say you've identified the common pattern, but that pattern itself is > a bad use case, and the use of private symbols as you have defined them > doesn't do anything to correct the technical issue. > > I think there’s been a misunderstanding. Everybody agrees that that’s a > bad pattern. It’s not what the point of private symbols would be. It’s not > a target use case. > > > Since you cannot stick new properties onto a non-extensible object, even > private symbols won't solve the problem with your use case. > > That appending private symbols to external objects which are frozen > wouldn’t work doesn’t matter precisely because it’s not a target use case. > That it doesn’t work reliably might even be considered a positive, since it > discourages something we all seem to agree is not good practice. > > It’s also not related to private symbols; this is already how properties > work, regardless of what kind of key they have. > > > The difference here is that in your use cases, library A is "sneakily" > storing information on object B. > > What use case are you referring to here? I can’t find any example in the > previous posts that matches these descriptions. As Isiah said, “all of the > examples here I've presented are for scenarios where the state is related > to the factory that created the objects.” The same is true of my examples. > Everybody’s on the same page regarding not wanting to add properties to > objects their own libraries do not create. > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180731/4bb29c1e/attachment.html>
You’re right, sorry — the SymbolTree example does operate on objects not created in the module itself, so my statement wasn’t accurate.
More carefully I ought to have said that the use cases concern object creation. Decorators and mixin functionality can fall in this bucket, where the object is likely not literally “birthed” by the library that is doing the decorating, yet the functionality is intended to be attached during that process by a consumer of the library.
In my own experience to date, all cases where I have run into the class-declaration scope limitation did concern locally created objects (class instances specifically), so yes, the adjustments you are talking about wrt the object members proposal probably would be able to cover them, though I would still tend to favor a more generic and simple solution.
You’re right, sorry — the SymbolTree example does operate on objects not created in the module itself, so my statement wasn’t accurate. More carefully I ought to have said that the use cases concern object creation. Decorators and mixin functionality can fall in this bucket, where the object is likely not literally “birthed” by the library that is doing the decorating, yet the functionality is intended to be attached during that process by a consumer of the library. In my own experience to date, all cases where I have run into the class-declaration scope limitation did concern locally created objects (class instances specifically), so yes, the adjustments you are talking about wrt the object members proposal probably would be able to cover them, though I would still tend to favor a more generic and simple solution. On Tue, Jul 31, 2018 at 3:09 PM Ranando King <kingmph at gmail.com> wrote: > > What use case are you referring to here? > > In the case of SymbolTree, the objects in use are external. > > > I think there’s been a misunderstanding. Everybody agrees that that’s a > bad pattern. It’s not what the point of private symbols would be. It’s not > a target use case. > > That certainly puts my mind at ease. > > > As Isiah said, “all of the examples here I've presented are for > scenarios where the state is related to the factory that created the > objects.” > > If the factory that creates the objects is the also the only thing trying > to store private information on those objects, then I understand you're > only looking for per-instance module-private data, possibly with the > ability to use common private names. If that's the case, then it really is > just 2 simple extensions of my proposal: > * allow a Symbol when used as a private or protected property name to > persist as the private Symbol name for the private instance field on each > object for which it is used. > * create an additional privilege level (internal) that places the new > field's name in the [[DeclarationInfo]] of the function containing the > declaration. > > The effect of using these 2 features together is that anything within the > same function as the declared Symbol will gain access to the internal field > of all objects using that Symbol as a field name. > > On Tue, Jul 31, 2018 at 1:36 PM Darien Valentine <valentinium at gmail.com> > wrote: > >> > I'd say you've identified the common pattern, but that pattern itself >> is a bad use case, and the use of private symbols as you have defined them >> doesn't do anything to correct the technical issue. >> >> I think there’s been a misunderstanding. Everybody agrees that that’s a >> bad pattern. It’s not what the point of private symbols would be. It’s not >> a target use case. >> >> > Since you cannot stick new properties onto a non-extensible object, >> even private symbols won't solve the problem with your use case. >> >> That appending private symbols to external objects which are frozen >> wouldn’t work doesn’t matter precisely because it’s not a target use case. >> That it doesn’t work reliably might even be considered a positive, since it >> discourages something we all seem to agree is not good practice. >> >> It’s also not related to private symbols; this is already how properties >> work, regardless of what kind of key they have. >> >> > The difference here is that in your use cases, library A is "sneakily" >> storing information on object B. >> >> What use case are you referring to here? I can’t find any example in the >> previous posts that matches these descriptions. As Isiah said, “all of the >> examples here I've presented are for scenarios where the state is related >> to the factory that created the objects.” The same is true of my examples. >> Everybody’s on the same page regarding not wanting to add properties to >> objects their own libraries do not create. >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180731/5a47f2a9/attachment-0001.html>
If you look at my proposal, it would be a "no" for the same reasons you can't add normal properties to them.
Isiah Meadows contact at isiahmeadows.com, www.isiahmeadows.com
If you look at my proposal, it would be a "no" for the same reasons you can't add normal properties to them. ----- Isiah Meadows contact at isiahmeadows.com www.isiahmeadows.com On Tue, Jul 31, 2018 at 1:17 PM, Michael Theriot <michael.lee.theriot at gmail.com> wrote: > Should a sealed/frozen object be privately extensible? I don't actually > know, but interesting point. > > > On Tuesday, July 31, 2018, Ranando King <kingmph at gmail.com> wrote: >> >> > Consider what people often use public symbols for now. >> >> I know that I use them as fixed-value unique keys (module globals) for >> properties on objects that I export and don't want others to be aware of or >> able to touch. >> >> > For example, consider this library [1]. In this case, they use a public >> > symbol for their stuff in this file [2]. >> >> And as I said before, if someone passes a non-extensible object to this >> library, it breaks. Since any library can request any object be sealed or >> frozen, the implementation of this library is too fragile. >> >> > Because here, it's not a cache, but it's literally extra associated data >> > in the object. And also, in that case, you *want* the engine to see it as a >> > property, since it can employ relevant IC caching for it. >> >> Here's a parallel for you. Right now, Google has a database with >> information about me on it. I don't have access to that information (module >> privacy) and that information isn't stored anywhere on me or my devices >> (module locality). This is a proper conceptual model. The information Google >> is keeping about me is information they generated. Why should I have to pay >> to store their information? Thankfully I don't. However, this is precisely >> what you're claiming to be a good use case. You want module privacy without >> module locality. If I were to play at a Kevin Gibbons-like response, I'd say >> you've identified the common pattern, but that pattern itself is a bad use >> case, and the use of private symbols as you have defined them doesn't do >> anything to correct the technical issue. Since you cannot stick new >> properties onto a non-extensible object, even private symbols won't solve >> the problem with your use case. >> >> > No, I'm not. I'm drawing a distinction between a pure many-to-one >> > association (weak maps) and a "has a" relationship (private symbol >> > properties). >> >> First, for any given property bag, the keys will need to be unique, but >> that doesn't force uniqueness onto the values. As such, even properties on >> an object provided by your "private Symbol" would still be many-1. When a >> 3rd party library wants to keep information associated with an arbitrary >> object, there are only 3 choices: >> * try to store that information on the object >> * this is what you're advocating, but it's not a good pattern. It's too >> fragile, being subject to break if the incoming object is not extensible. >> * store the information as being associated to the object (WeakMap) >> * this is the pattern that works in all cases (but the syntax is >> cumbersome and the implementation somewhat slow) >> * return a wrapper containing the original object and the new information >> (Proxy or custom wrapper) >> * this is another possibility, but requires that any users accept and >> use the new Proxy or wrapper object in lieu of the original. >> >> > Another scenario is for JSDOM's `Window` implementation, where they have >> > a few underscore-private variables like this [3]. That particular variable >> > is used in several disparate parts throughout the code base [4], but is >> > still conceptually a property. This is a case where a private symbol >> > property is appropriate. >> >> It's not just "conceptually" a property. It's logically a property. Why? >> Because all the objects that it exists on were constructed somewhere within >> the JSDOM library. That's me putting _my_ keys in _my_ pocket. There's >> absolutely nothing wrong with that. >> >> > Conversely in this JSDOM file [5], it's just associating data with an >> > arbitrary object it happens to have, and so using the weak map makes perfect >> > sense. >> >> Conceptually speaking, this is the same scenario as SymbolTree. In both >> cases, the library is generating information associated with an object it >> doesn't own and didn't create. >> >> > BTW, you could make a similar argument against superclass private fields >> > - it's like hiding valuable info in your wallet before you receive it for >> > the first time, but even after dismantling it, you can't find any evidence >> > of that valuable info. >> >> That dog don't hunt. The difference here is that in your use cases, >> library A is "sneakily" storing information on object B. In the case of >> superclass private fields, subclass B has "volunteered" to take on the >> information and functionality of class A. You've essentially compared apples >> and asteroids just because they both begin with "a". >> >> On Tue, Jul 31, 2018 at 2:15 AM Isiah Meadows <isiahmeadows at gmail.com> >> wrote: >>> >>> > Isn't this precisely what WeakMaps are for? If the data is >>> > "module-internal", then the module needs to be the owner of the data >>> > store. >>> > If the data is about "arbitrary objects" (object from outside the >>> > module?) >>> > then those objects are the keys to the data store. If any object is >>> > thrown >>> > away, the associated data is no longer needed. If this doesn't fit the >>> > functionality of a WeakMap, I don't know what will. >>> >>> Consider what people often use public symbols for now. For example, >>> consider this library [1]. In this case, they use a public symbol for >>> their stuff in this file [2]. >>> >>> But here's the thing: that doesn't really need discoverable, and is a >>> pure implementation detail. Wouldn't it make more sense for them to >>> just use a private symbol instead? Because here, it's not a cache, but >>> it's literally extra associated data in the object. And also, in that >>> case, you *want* the engine to see it as a property, since it can >>> employ relevant IC caching for it. >>> >>> > Isn't that precisely what your question calls for? You're caching >>> > module-internal data about external objects. >>> >>> No, I'm not. I'm drawing a distinction between a pure many-to-one >>> association (weak maps) and a "has a" relationship (private symbol >>> properties). You *could* implement one in terms of the other, but >>> these two types of relationships are *completely* different at a >>> conceptual level and how you model them. >>> >>> For js-symbol-tree, it's not simply associating a node to a value, but >>> setting up the object so it *has* the data required for a doubly >>> linked list tree node. Because this symbol is repeatedly accessed, >>> it's not caching so much as it's adding data the object needs for it >>> to do what it needs to do. >>> >>> Another scenario is for JSDOM's `Window` implementation, where they >>> have a few underscore-private variables like this [3]. That particular >>> variable is used in several disparate parts throughout the code base >>> [4], but is still conceptually a property. This is a case where a >>> private symbol property is appropriate. >>> >>> Conversely in this JSDOM file [5], it's just associating data with an >>> arbitrary object it happens to have, and so using the weak map makes >>> perfect sense. >>> >>> > Likewise, I'm specifically against the abuse of objects to store state >>> > unrelated to the factory that created it. To me, that's as if I came to >>> > visit you and somehow you managed to hide some valuable info in my >>> > wallet >>> > without me noticing, and even if I completely dismantle my wallet, I >>> > wouldn't be able to find it. But somehow you can easily retrieve it the >>> > next >>> > time I come around. That's just conceptually weird. >>> >>> All of the examples here I've presented are for scenarios where the >>> state *is* related to the factory that created the objects. It's not >>> *directly* related (and thus encapsulation is warranted), but it's >>> still *related*, enough so that you usually see the state initialized >>> within the creator's constructor call. It's about as related as the >>> superclass is to a subclass of it. >>> >>> BTW, you could make a similar argument against superclass private >>> fields - it's like hiding valuable info in your wallet before you >>> receive it for the first time, but even after dismantling it, you >>> can't find any evidence of that valuable info. >>> >>> [1]: https://github.com/jsdom/js-symbol-tree >>> [2]: >>> https://github.com/jsdom/js-symbol-tree/blob/master/lib/SymbolTree.js#L28 >>> [3]: >>> https://github.com/jsdom/jsdom/blob/23d67ebec901b3471b84e63f58a96b51a36f3671/lib/jsdom/browser/Window.js#L80 >>> [4]: https://github.com/jsdom/jsdom/search?q=_globalProxy >>> [5]: >>> https://github.com/jsdom/jsdom/blob/ad0e551b1b633e07d11f98d7a30287491958def3/lib/jsdom/living/websockets/WebSocket-impl.js#L49 >>> >>> ----- >>> >>> Isiah Meadows >>> contact at isiahmeadows.com >>> www.isiahmeadows.com >>> >>> >>> On Tue, Jul 31, 2018 at 1:55 AM, Ranando King <kingmph at gmail.com> wrote: >>> >> One last thing: how would you hope to deal with module-internal data >>> >> stored on arbitrary objects, using any means other than private >>> >> symbols or >>> >> something similar? >>> > >>> > Isn't this precisely what WeakMaps are for? If the data is >>> > "module-internal", then the module needs to be the owner of the data >>> > store. >>> > If the data is about "arbitrary objects" (object from outside the >>> > module?) >>> > then those objects are the keys to the data store. If any object is >>> > thrown >>> > away, the associated data is no longer needed. If this doesn't fit the >>> > functionality of a WeakMap, I don't know what will. >>> > >>> >> Weak maps make sense when the weak map is the dictionary conceptually >>> >> (think: caching). >>> > >>> > Isn't that precisely what your question calls for? You're caching >>> > module-internal data about external objects. >>> > >>> >> Keep in mind, I'm specifically *against* the abuse of weak maps for >>> >> private state that's conceptually (in an abstract sense, not runtime) >>> >> part >>> >> of an object. >>> > >>> > Likewise, I'm specifically against the abuse of objects to store state >>> > unrelated to the factory that created it. To me, that's as if I came to >>> > visit you and somehow you managed to hide some valuable info in my >>> > wallet >>> > without me noticing, and even if I completely dismantle my wallet, I >>> > wouldn't be able to find it. But somehow you can easily retrieve it the >>> > next >>> > time I come around. That's just conceptually weird. >>> > >>> > On Mon, Jul 30, 2018 at 9:42 PM Isiah Meadows <isiahmeadows at gmail.com> >>> > wrote: >>> >> >>> >> The reason private symbols are appropriate for Node's use case is >>> >> because it's conceptually a mixin, not a simple key/value map with >>> >> various utility functions (and weak map lookup is slower than property >>> >> access). JSDOM uses a similar utility [1] as a sort of mixin. >>> >> >>> >> Keep in mind, I'm specifically *against* the abuse of weak maps for >>> >> private state that's conceptually (in an abstract sense, not runtime) >>> >> part of an object. Weak maps make sense when the weak map is the >>> >> dictionary conceptually (think: caching). But if conceptually, the >>> >> object is the dictionary, putting it in a weak map is giving the >>> >> engine the wrong info - properties have inline caches and heavy >>> >> optimization, but you can't do the same for weak maps in the other >>> >> direction without literally implementing them as properties. (I would >>> >> *love* to be proven wrong here, BTW.) >>> >> >>> >> Let me draw a quick comparison: When do you use a map/set with string >>> >> keys, and when do you use an object instead? >>> >> >>> >> - Both are functionally equivalent, but engines use *very* different >>> >> algorithms for each one. >>> >> - I can almost guarantee you don't use maps when object properties >>> >> work. >>> >> >>> >> One last thing: how would you hope to deal with module-internal data >>> >> stored on arbitrary objects, using any means other than private >>> >> symbols or something similar? To clarify, I'm talking of opaque object >>> >> structs [2], not simply classes. (BTW, that one is easier to manage as >>> >> a struct rather than a class, because of how many "methods" there are >>> >> operating on the state.) >>> >> >>> >> [1]: https://github.com/jsdom/js-symbol-tree >>> >> [2]: https://github.com/isiahmeadows/enigma/blob/master/src/parser.ts >>> >> >>> >> ----- >>> >> >>> >> Isiah Meadows >>> >> contact at isiahmeadows.com >>> >> www.isiahmeadows.com >>> >> >>> >> >>> >> On Mon, Jul 30, 2018 at 9:00 PM, Ranando King <kingmph at gmail.com> >>> >> wrote: >>> >> > I meant to say if the object passed to the 3rd party function..... >>> >> > >>> >> > >>> >> > On Mon, Jul 30, 2018 at 7:59 PM Ranando King <kingmph at gmail.com> >>> >> > wrote: >>> >> >> >>> >> >> Just that use case alone is problematic. If the 3rd party function >>> >> >> is >>> >> >> not >>> >> >> extensible, then the new private data should not be allowed. If the >>> >> >> library >>> >> >> cannot function without storing that data, then the function will >>> >> >> have >>> >> >> no >>> >> >> choice but to fall back to WeakMaps which don't care if the key is >>> >> >> not >>> >> >> extensible. So why not just stick with WeakMaps for that case? And >>> >> >> if >>> >> >> that's >>> >> >> the case, then there would be little need for so open a means of >>> >> >> defining >>> >> >> private field names. The proposal I'm offering offers the room to >>> >> >> extend it >>> >> >> in the future to support everything else you might look for from >>> >> >> your >>> >> >> private symbols idea.... unless you think I missed something. >>> >> >> >>> >> >> On Mon, Jul 30, 2018 at 7:26 PM Isiah Meadows >>> >> >> <isiahmeadows at gmail.com> >>> >> >> wrote: >>> >> >>> >>> >> >>> That is one supported use case, yes. But that isn't the only use >>> >> >>> case >>> >> >>> this supports. It can still extend to traditional private class >>> >> >>> data, >>> >> >>> too. >>> >> >>> >>> >> >>> ----- >>> >> >>> >>> >> >>> Isiah Meadows >>> >> >>> contact at isiahmeadows.com >>> >> >>> www.isiahmeadows.com >>> >> >>> >>> >> >>> >>> >> >>> On Mon, Jul 30, 2018 at 8:04 PM, Ranando King <kingmph at gmail.com> >>> >> >>> wrote: >>> >> >>> > So you're wanting the ability for a 3rd-party function to be >>> >> >>> > able to >>> >> >>> > store >>> >> >>> > data private to that library on an object it didn't create, and >>> >> >>> > that >>> >> >>> > only >>> >> >>> > that library can access? >>> >> >>> > >>> >> >>> > On Mon, Jul 30, 2018 at 6:36 PM Isiah Meadows >>> >> >>> > <isiahmeadows at gmail.com> >>> >> >>> > wrote: >>> >> >>> >> >>> >> >>> >> First, my private symbols are properly *private*. The only >>> >> >>> >> "unexpected" thing that could happen is making an object larger >>> >> >>> >> memory-wise, which engines already have to be equipped to >>> >> >>> >> handle >>> >> >>> >> now >>> >> >>> >> (libraries aren't always well-behaved, and like to occasionally >>> >> >>> >> add >>> >> >>> >> expando properties to builtins and DOM elements). About the >>> >> >>> >> only >>> >> >>> >> thing >>> >> >>> >> most people would care about is in the debugger. >>> >> >>> >> >>> >> >>> >> Second, I had things like this in mind with supporting expando >>> >> >>> >> properties: >>> >> >>> >> >>> >> >>> >> >>> >> >>> >> >>> >> >>> >> https://github.com/nodejs/node/blob/ae4fde8bc883686def5badfb324236320669e8f4/lib/internal/linkedlist.js >>> >> >>> >> >>> >> >>> >> In that case, the Node.js people made it a pseudo-mixin rather >>> >> >>> >> than >>> >> >>> >> an >>> >> >>> >> actual type for performance reasons - there's fewer object >>> >> >>> >> allocations >>> >> >>> >> and they needed that. >>> >> >>> >> >>> >> >>> >> So I've considered the expando problem, and I disagree about it >>> >> >>> >> being >>> >> >>> >> a problem at all. >>> >> >>> >> >>> >> >>> >> ----- >>> >> >>> >> >>> >> >>> >> Isiah Meadows >>> >> >>> >> contact at isiahmeadows.com >>> >> >>> >> www.isiahmeadows.com >>> >> >>> >> >>> >> >>> >> >>> >> >>> >> On Mon, Jul 30, 2018 at 6:35 PM, Waldemar Horwat >>> >> >>> >> <waldemar at google.com> >>> >> >>> >> wrote: >>> >> >>> >> > On 07/29/2018 04:37 PM, Isiah Meadows wrote: >>> >> >>> >> >> >>> >> >>> >> >> BTW, I came up with an alternate proposal for privacy >>> >> >>> >> >> altogether: >>> >> >>> >> >> https://github.com/tc39/proposal-class-fields/issues/115 >>> >> >>> >> >> >>> >> >>> >> >> TL;DR: private symbols that proxies can't see and that can't >>> >> >>> >> >> be >>> >> >>> >> >> enumerated. >>> >> >>> >> > >>> >> >>> >> > >>> >> >>> >> > Aside from syntax, the main semantic difference I see between >>> >> >>> >> > this >>> >> >>> >> > alternative and the main one is that this alternative defines >>> >> >>> >> > private >>> >> >>> >> > fields >>> >> >>> >> > as expandos, creating opportunities for mischief by attaching >>> >> >>> >> > them >>> >> >>> >> > to >>> >> >>> >> > unexpected objects. Aside from privacy, one of the things >>> >> >>> >> > the >>> >> >>> >> > private >>> >> >>> >> > fields proposal gives you is consistency among multiple >>> >> >>> >> > private >>> >> >>> >> > fields >>> >> >>> >> > on >>> >> >>> >> > the same object. In the rare cases where you don't want >>> >> >>> >> > that, >>> >> >>> >> > you >>> >> >>> >> > could >>> >> >>> >> > use >>> >> >>> >> > weak maps. >>> >> >>> >> > >>> >> >>> >> > Waldemar >>> >> >>> >> _______________________________________________ >>> >> >>> >> 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 >
I get where you're coming from. The main reasons I've written my proposal this way are:
- Prior art:
- Many ES developers come from other, class-based, object-oriented languages where keywords are the primary way of controlling data accessibility. This means using the well known keywords will lower the learning curve and increase adoption.
- Future expansion:
- Careful and narrow definition of the keywords and their corresponding actions allows undesirable patterns to be avoided while leaving room for future extensibility.
The argument I raised with Isiah was just 1 example of a bad pattern that being too generic can open up. These are also part of the reasons why I am against proposal-class-fields. It seems so simple on the surface, but effectively makes an already limited keyword even more limited than is should be. Plus it adds difficulty to creating new features around those concepts in a way that will be easily understood by developers migrating from other languages.
I get where you're coming from. The main reasons I've written my proposal this way are: * Prior art: - Many ES developers come from other, class-based, object-oriented languages where keywords are the primary way of controlling data accessibility. This means using the well known keywords will lower the learning curve and increase adoption. * Future expansion: - Careful and narrow definition of the keywords and their corresponding actions allows undesirable patterns to be avoided while leaving room for future extensibility. The argument I raised with Isiah was just 1 example of a bad pattern that being too generic can open up. These are also part of the reasons why I am against proposal-class-fields. It seems so simple on the surface, but effectively makes an already limited keyword even more limited than is should be. Plus it adds difficulty to creating new features around those concepts in a way that will be easily understood by developers migrating from other languages. On Tue, Jul 31, 2018 at 3:43 PM Darien Valentine <valentinium at gmail.com> wrote: > You’re right, sorry — the SymbolTree example does operate on objects not > created in the module itself, so my statement wasn’t accurate. > > More carefully I ought to have said that the use cases concern object > creation. Decorators and mixin functionality can fall in this bucket, where > the object is likely not literally “birthed” by the library that is doing > the decorating, yet the functionality is intended to be attached during > that process by a consumer of the library. > > In my own experience to date, all cases where I have run into the > class-declaration scope limitation did concern locally created objects > (class instances specifically), so yes, the adjustments you are talking > about wrt the object members proposal probably would be able to cover them, > though I would still tend to favor a more generic and simple solution. > > On Tue, Jul 31, 2018 at 3:09 PM Ranando King <kingmph at gmail.com> wrote: > >> > What use case are you referring to here? >> >> In the case of SymbolTree, the objects in use are external. >> >> > I think there’s been a misunderstanding. Everybody agrees that that’s >> a bad pattern. It’s not what the point of private symbols would be. It’s >> not a target use case. >> >> That certainly puts my mind at ease. >> >> > As Isiah said, “all of the examples here I've presented are for >> scenarios where the state is related to the factory that created the >> objects.” >> >> If the factory that creates the objects is the also the only thing trying >> to store private information on those objects, then I understand you're >> only looking for per-instance module-private data, possibly with the >> ability to use common private names. If that's the case, then it really is >> just 2 simple extensions of my proposal: >> * allow a Symbol when used as a private or protected property name to >> persist as the private Symbol name for the private instance field on each >> object for which it is used. >> * create an additional privilege level (internal) that places the new >> field's name in the [[DeclarationInfo]] of the function containing the >> declaration. >> >> The effect of using these 2 features together is that anything within the >> same function as the declared Symbol will gain access to the internal field >> of all objects using that Symbol as a field name. >> >> On Tue, Jul 31, 2018 at 1:36 PM Darien Valentine <valentinium at gmail.com> >> wrote: >> >>> > I'd say you've identified the common pattern, but that pattern itself >>> is a bad use case, and the use of private symbols as you have defined them >>> doesn't do anything to correct the technical issue. >>> >>> I think there’s been a misunderstanding. Everybody agrees that that’s a >>> bad pattern. It’s not what the point of private symbols would be. It’s not >>> a target use case. >>> >>> > Since you cannot stick new properties onto a non-extensible object, >>> even private symbols won't solve the problem with your use case. >>> >>> That appending private symbols to external objects which are frozen >>> wouldn’t work doesn’t matter precisely because it’s not a target use case. >>> That it doesn’t work reliably might even be considered a positive, since it >>> discourages something we all seem to agree is not good practice. >>> >>> It’s also not related to private symbols; this is already how properties >>> work, regardless of what kind of key they have. >>> >>> > The difference here is that in your use cases, library A is "sneakily" >>> storing information on object B. >>> >>> What use case are you referring to here? I can’t find any example in the >>> previous posts that matches these descriptions. As Isiah said, “all of the >>> examples here I've presented are for scenarios where the state is related >>> to the factory that created the objects.” The same is true of my examples. >>> Everybody’s on the same page regarding not wanting to add properties to >>> objects their own libraries do not create. >>> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180731/5c198686/attachment-0001.html>
If you go back a few months, what you're proposing is very similar, at least functionally, to my previous iteration of my proposal: isiahmeadows/private-symbol-proposal/blob/c5c9781d9e76123c92d8fbc83681fdd3a9b0b319/README.md
My main problem was that trying to limit private properties to objects created within a scope got complicated in a hurry once you considered all the small details, and it just didn't seem simple anymore. It only got more complicated when you started getting into the logistics of integrating with modules.
So I've considered the issue and explored it pretty thoroughly - I really don't want private data to be limited to classes (which I dislike), but I did also previously have the concern of trying to limit who could define properties where.
I will point out that you can prevent arbitrary private extension by
simply doing Object.preventExtensions(object)
. Because properties
defined using private symbols are otherwise just normal properties,
they still have to go through the same access checks normal properties
have to, like [[IsExtensible]]. The only other concrete difference is
that proxy hooks don't fire when you do things with private symbols.
Isiah Meadows contact at isiahmeadows.com, www.isiahmeadows.com
If you go back a few months, what you're proposing is *very* similar, at least functionally, to my previous iteration of my proposal: https://github.com/isiahmeadows/private-symbol-proposal/blob/c5c9781d9e76123c92d8fbc83681fdd3a9b0b319/README.md My main problem was that trying to limit private properties to objects created within a scope got complicated in a hurry once you considered all the small details, and it just didn't seem simple anymore. It only got more complicated when you started getting into the logistics of integrating with modules. So I've considered the issue and explored it pretty thoroughly - I *really* don't want private data to be limited to classes (which I dislike), but I did also previously have the concern of trying to limit who could define properties where. I will point out that you can prevent arbitrary private extension by simply doing `Object.preventExtensions(object)`. Because properties defined using private symbols are otherwise just normal properties, they still have to go through the same access checks normal properties have to, like [[IsExtensible]]. The only other concrete difference is that proxy hooks don't fire when you do things with private symbols. ----- Isiah Meadows contact at isiahmeadows.com www.isiahmeadows.com On Tue, Jul 31, 2018 at 3:09 PM, Ranando King <kingmph at gmail.com> wrote: >> What use case are you referring to here? > > In the case of SymbolTree, the objects in use are external. > >> I think there’s been a misunderstanding. Everybody agrees that that’s a >> bad pattern. It’s not what the point of private symbols would be. It’s not a >> target use case. > > That certainly puts my mind at ease. > >> As Isiah said, “all of the examples here I've presented are for scenarios >> where the state is related to the factory that created the objects.” > > If the factory that creates the objects is the also the only thing trying to > store private information on those objects, then I understand you're only > looking for per-instance module-private data, possibly with the ability to > use common private names. If that's the case, then it really is just 2 > simple extensions of my proposal: > * allow a Symbol when used as a private or protected property name to > persist as the private Symbol name for the private instance field on each > object for which it is used. > * create an additional privilege level (internal) that places the new > field's name in the [[DeclarationInfo]] of the function containing the > declaration. > > The effect of using these 2 features together is that anything within the > same function as the declared Symbol will gain access to the internal field > of all objects using that Symbol as a field name. > > On Tue, Jul 31, 2018 at 1:36 PM Darien Valentine <valentinium at gmail.com> > wrote: >> >> > I'd say you've identified the common pattern, but that pattern itself is >> > a bad use case, and the use of private symbols as you have defined them >> > doesn't do anything to correct the technical issue. >> >> I think there’s been a misunderstanding. Everybody agrees that that’s a >> bad pattern. It’s not what the point of private symbols would be. It’s not a >> target use case. >> >> > Since you cannot stick new properties onto a non-extensible object, even >> > private symbols won't solve the problem with your use case. >> >> That appending private symbols to external objects which are frozen >> wouldn’t work doesn’t matter precisely because it’s not a target use case. >> That it doesn’t work reliably might even be considered a positive, since it >> discourages something we all seem to agree is not good practice. >> >> It’s also not related to private symbols; this is already how properties >> work, regardless of what kind of key they have. >> >> > The difference here is that in your use cases, library A is "sneakily" >> > storing information on object B. >> >> What use case are you referring to here? I can’t find any example in the >> previous posts that matches these descriptions. As Isiah said, “all of the >> examples here I've presented are for scenarios where the state is related to >> the factory that created the objects.” The same is true of my examples. >> Everybody’s on the same page regarding not wanting to add properties to >> objects their own libraries do not create.
Note that builtins with internal slots, like Map, Set, and Promise, are still mutable after being frozen - so if one is trying to model internal slots with some kind of property stored on the object, then freezing must have no effect on the ability to alter their contents.
Note that builtins with internal slots, like Map, Set, and Promise, are still mutable after being frozen - so if one is trying to model internal slots with some kind of property stored on the object, then freezing *must* have no effect on the ability to alter their contents. On Tue, Jul 31, 2018 at 2:34 PM, Isiah Meadows <isiahmeadows at gmail.com> wrote: > If you go back a few months, what you're proposing is *very* similar, > at least functionally, to my previous iteration of my proposal: > https://github.com/isiahmeadows/private-symbol-proposal/blob/ > c5c9781d9e76123c92d8fbc83681fdd3a9b0b319/README.md > > My main problem was that trying to limit private properties to objects > created within a scope got complicated in a hurry once you considered > all the small details, and it just didn't seem simple anymore. It only > got more complicated when you started getting into the logistics of > integrating with modules. > > So I've considered the issue and explored it pretty thoroughly - I > *really* don't want private data to be limited to classes (which I > dislike), but I did also previously have the concern of trying to > limit who could define properties where. > > I will point out that you can prevent arbitrary private extension by > simply doing `Object.preventExtensions(object)`. Because properties > defined using private symbols are otherwise just normal properties, > they still have to go through the same access checks normal properties > have to, like [[IsExtensible]]. The only other concrete difference is > that proxy hooks don't fire when you do things with private symbols. > > ----- > > Isiah Meadows > contact at isiahmeadows.com > www.isiahmeadows.com > > > On Tue, Jul 31, 2018 at 3:09 PM, Ranando King <kingmph at gmail.com> wrote: > >> What use case are you referring to here? > > > > In the case of SymbolTree, the objects in use are external. > > > >> I think there’s been a misunderstanding. Everybody agrees that that’s a > >> bad pattern. It’s not what the point of private symbols would be. It’s > not a > >> target use case. > > > > That certainly puts my mind at ease. > > > >> As Isiah said, “all of the examples here I've presented are for > scenarios > >> where the state is related to the factory that created the objects.” > > > > If the factory that creates the objects is the also the only thing > trying to > > store private information on those objects, then I understand you're only > > looking for per-instance module-private data, possibly with the ability > to > > use common private names. If that's the case, then it really is just 2 > > simple extensions of my proposal: > > * allow a Symbol when used as a private or protected property name to > > persist as the private Symbol name for the private instance field on each > > object for which it is used. > > * create an additional privilege level (internal) that places the new > > field's name in the [[DeclarationInfo]] of the function containing the > > declaration. > > > > The effect of using these 2 features together is that anything within the > > same function as the declared Symbol will gain access to the internal > field > > of all objects using that Symbol as a field name. > > > > On Tue, Jul 31, 2018 at 1:36 PM Darien Valentine <valentinium at gmail.com> > > wrote: > >> > >> > I'd say you've identified the common pattern, but that pattern itself > is > >> > a bad use case, and the use of private symbols as you have defined > them > >> > doesn't do anything to correct the technical issue. > >> > >> I think there’s been a misunderstanding. Everybody agrees that that’s a > >> bad pattern. It’s not what the point of private symbols would be. It’s > not a > >> target use case. > >> > >> > Since you cannot stick new properties onto a non-extensible object, > even > >> > private symbols won't solve the problem with your use case. > >> > >> That appending private symbols to external objects which are frozen > >> wouldn’t work doesn’t matter precisely because it’s not a target use > case. > >> That it doesn’t work reliably might even be considered a positive, > since it > >> discourages something we all seem to agree is not good practice. > >> > >> It’s also not related to private symbols; this is already how properties > >> work, regardless of what kind of key they have. > >> > >> > The difference here is that in your use cases, library A is "sneakily" > >> > storing information on object B. > >> > >> What use case are you referring to here? I can’t find any example in the > >> previous posts that matches these descriptions. As Isiah said, “all of > the > >> examples here I've presented are for scenarios where the state is > related to > >> the factory that created the objects.” The same is true of my examples. > >> Everybody’s on the same page regarding not wanting to add properties to > >> objects their own libraries do not create. > _______________________________________________ > 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/20180731/e7eb4738/attachment.html>
The argument I raised with Isiah was just 1 example of a bad pattern that being too generic can open up.
BTW, the risk for bad patterns doesn't necessarily justify exclusion
of a feature. As I've brought up here before, iterators can be
seriously abused similarly 1, thanks to the availability of throw
return
, and you can always make a deferred out of a promise by simply pulling theresolve
/reject
out of the promise callback's scope. As long as it looks strange and weird enough and we have more semantically appropriate alternatives, I don't think we have an issue with potential abuse, and I feel private symbols as I have them meet this threshold.
Isiah Meadows contact at isiahmeadows.com, www.isiahmeadows.com
> The argument I raised with Isiah was just 1 example of a bad pattern that > being too generic can open up. BTW, the risk for bad patterns doesn't necessarily justify exclusion of a feature. As I've brought up here before, iterators can be seriously abused similarly [1], thanks to the availability of `throw` + `return`, and you can always make a deferred out of a promise by simply pulling the `resolve`/`reject` out of the promise callback's scope. As long as it looks strange and weird enough and we have more semantically appropriate alternatives, I don't think we have an issue with potential abuse, and I feel private symbols as I have them meet this threshold. [1]: https://github.com/tc39/proposal-async-iteration/issues/68 ----- Isiah Meadows contact at isiahmeadows.com www.isiahmeadows.com On Tue, Jul 31, 2018 at 5:10 PM, Ranando King <kingmph at gmail.com> wrote: > I get where you're coming from. The main reasons I've written my proposal > this way are: > > * Prior art: > - Many ES developers come from other, class-based, object-oriented > languages where keywords are the primary way of controlling data > accessibility. This means using the well known keywords will lower the > learning curve and increase adoption. > * Future expansion: > - Careful and narrow definition of the keywords and their corresponding > actions allows undesirable patterns to be avoided while leaving room for > future extensibility. > > The argument I raised with Isiah was just 1 example of a bad pattern that > being too generic can open up. These are also part of the reasons why I am > against proposal-class-fields. It seems so simple on the surface, but > effectively makes an already limited keyword even more limited than is > should be. Plus it adds difficulty to creating new features around those > concepts in a way that will be easily understood by developers migrating > from other languages. > > > On Tue, Jul 31, 2018 at 3:43 PM Darien Valentine <valentinium at gmail.com> > wrote: >> >> You’re right, sorry — the SymbolTree example does operate on objects not >> created in the module itself, so my statement wasn’t accurate. >> >> More carefully I ought to have said that the use cases concern object >> creation. Decorators and mixin functionality can fall in this bucket, where >> the object is likely not literally “birthed” by the library that is doing >> the decorating, yet the functionality is intended to be attached during that >> process by a consumer of the library. >> >> In my own experience to date, all cases where I have run into the >> class-declaration scope limitation did concern locally created objects >> (class instances specifically), so yes, the adjustments you are talking >> about wrt the object members proposal probably would be able to cover them, >> though I would still tend to favor a more generic and simple solution. >> >> On Tue, Jul 31, 2018 at 3:09 PM Ranando King <kingmph at gmail.com> wrote: >>> >>> > What use case are you referring to here? >>> >>> In the case of SymbolTree, the objects in use are external. >>> >>> > I think there’s been a misunderstanding. Everybody agrees that that’s a >>> > bad pattern. It’s not what the point of private symbols would be. It’s not a >>> > target use case. >>> >>> That certainly puts my mind at ease. >>> >>> > As Isiah said, “all of the examples here I've presented are for >>> > scenarios where the state is related to the factory that created the >>> > objects.” >>> >>> If the factory that creates the objects is the also the only thing trying >>> to store private information on those objects, then I understand you're only >>> looking for per-instance module-private data, possibly with the ability to >>> use common private names. If that's the case, then it really is just 2 >>> simple extensions of my proposal: >>> * allow a Symbol when used as a private or protected property name to >>> persist as the private Symbol name for the private instance field on each >>> object for which it is used. >>> * create an additional privilege level (internal) that places the new >>> field's name in the [[DeclarationInfo]] of the function containing the >>> declaration. >>> >>> The effect of using these 2 features together is that anything within the >>> same function as the declared Symbol will gain access to the internal field >>> of all objects using that Symbol as a field name. >>> >>> On Tue, Jul 31, 2018 at 1:36 PM Darien Valentine <valentinium at gmail.com> >>> wrote: >>>> >>>> > I'd say you've identified the common pattern, but that pattern itself >>>> > is a bad use case, and the use of private symbols as you have defined them >>>> > doesn't do anything to correct the technical issue. >>>> >>>> I think there’s been a misunderstanding. Everybody agrees that that’s a >>>> bad pattern. It’s not what the point of private symbols would be. It’s not a >>>> target use case. >>>> >>>> > Since you cannot stick new properties onto a non-extensible object, >>>> > even private symbols won't solve the problem with your use case. >>>> >>>> That appending private symbols to external objects which are frozen >>>> wouldn’t work doesn’t matter precisely because it’s not a target use case. >>>> That it doesn’t work reliably might even be considered a positive, since it >>>> discourages something we all seem to agree is not good practice. >>>> >>>> It’s also not related to private symbols; this is already how properties >>>> work, regardless of what kind of key they have. >>>> >>>> > The difference here is that in your use cases, library A is "sneakily" >>>> > storing information on object B. >>>> >>>> What use case are you referring to here? I can’t find any example in the >>>> previous posts that matches these descriptions. As Isiah said, “all of the >>>> examples here I've presented are for scenarios where the state is related to >>>> the factory that created the objects.” The same is true of my examples. >>>> Everybody’s on the same page regarding not wanting to add properties to >>>> objects their own libraries do not create. > > > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss >
Yeah, I left it without saying that you could just model them as having their state as a single private symbol field with all the relevant data for it. I assumed it would be obvious enough for those who really pay attention to the spec, so I just left it implied.
Isiah Meadows contact at isiahmeadows.com, www.isiahmeadows.com
Yeah, I left it without saying that you could just model them as having their state as a single private symbol field with all the relevant data for it. I assumed it would be obvious enough for those who really pay attention to the spec, so I just left it implied. ----- Isiah Meadows contact at isiahmeadows.com www.isiahmeadows.com On Tue, Jul 31, 2018 at 5:40 PM, Jordan Harband <ljharb at gmail.com> wrote: > Note that builtins with internal slots, like Map, Set, and Promise, are > still mutable after being frozen - so if one is trying to model internal > slots with some kind of property stored on the object, then freezing *must* > have no effect on the ability to alter their contents. > > On Tue, Jul 31, 2018 at 2:34 PM, Isiah Meadows <isiahmeadows at gmail.com> > wrote: >> >> If you go back a few months, what you're proposing is *very* similar, >> at least functionally, to my previous iteration of my proposal: >> >> https://github.com/isiahmeadows/private-symbol-proposal/blob/c5c9781d9e76123c92d8fbc83681fdd3a9b0b319/README.md >> >> My main problem was that trying to limit private properties to objects >> created within a scope got complicated in a hurry once you considered >> all the small details, and it just didn't seem simple anymore. It only >> got more complicated when you started getting into the logistics of >> integrating with modules. >> >> So I've considered the issue and explored it pretty thoroughly - I >> *really* don't want private data to be limited to classes (which I >> dislike), but I did also previously have the concern of trying to >> limit who could define properties where. >> >> I will point out that you can prevent arbitrary private extension by >> simply doing `Object.preventExtensions(object)`. Because properties >> defined using private symbols are otherwise just normal properties, >> they still have to go through the same access checks normal properties >> have to, like [[IsExtensible]]. The only other concrete difference is >> that proxy hooks don't fire when you do things with private symbols. >> >> ----- >> >> Isiah Meadows >> contact at isiahmeadows.com >> www.isiahmeadows.com >> >> >> On Tue, Jul 31, 2018 at 3:09 PM, Ranando King <kingmph at gmail.com> wrote: >> >> What use case are you referring to here? >> > >> > In the case of SymbolTree, the objects in use are external. >> > >> >> I think there’s been a misunderstanding. Everybody agrees that that’s a >> >> bad pattern. It’s not what the point of private symbols would be. It’s >> >> not a >> >> target use case. >> > >> > That certainly puts my mind at ease. >> > >> >> As Isiah said, “all of the examples here I've presented are for >> >> scenarios >> >> where the state is related to the factory that created the objects.” >> > >> > If the factory that creates the objects is the also the only thing >> > trying to >> > store private information on those objects, then I understand you're >> > only >> > looking for per-instance module-private data, possibly with the ability >> > to >> > use common private names. If that's the case, then it really is just 2 >> > simple extensions of my proposal: >> > * allow a Symbol when used as a private or protected property name to >> > persist as the private Symbol name for the private instance field on >> > each >> > object for which it is used. >> > * create an additional privilege level (internal) that places the new >> > field's name in the [[DeclarationInfo]] of the function containing the >> > declaration. >> > >> > The effect of using these 2 features together is that anything within >> > the >> > same function as the declared Symbol will gain access to the internal >> > field >> > of all objects using that Symbol as a field name. >> > >> > On Tue, Jul 31, 2018 at 1:36 PM Darien Valentine <valentinium at gmail.com> >> > wrote: >> >> >> >> > I'd say you've identified the common pattern, but that pattern itself >> >> > is >> >> > a bad use case, and the use of private symbols as you have defined >> >> > them >> >> > doesn't do anything to correct the technical issue. >> >> >> >> I think there’s been a misunderstanding. Everybody agrees that that’s a >> >> bad pattern. It’s not what the point of private symbols would be. It’s >> >> not a >> >> target use case. >> >> >> >> > Since you cannot stick new properties onto a non-extensible object, >> >> > even >> >> > private symbols won't solve the problem with your use case. >> >> >> >> That appending private symbols to external objects which are frozen >> >> wouldn’t work doesn’t matter precisely because it’s not a target use >> >> case. >> >> That it doesn’t work reliably might even be considered a positive, >> >> since it >> >> discourages something we all seem to agree is not good practice. >> >> >> >> It’s also not related to private symbols; this is already how >> >> properties >> >> work, regardless of what kind of key they have. >> >> >> >> > The difference here is that in your use cases, library A is >> >> > "sneakily" >> >> > storing information on object B. >> >> >> >> What use case are you referring to here? I can’t find any example in >> >> the >> >> previous posts that matches these descriptions. As Isiah said, “all of >> >> the >> >> examples here I've presented are for scenarios where the state is >> >> related to >> >> the factory that created the objects.” The same is true of my examples. >> >> Everybody’s on the same page regarding not wanting to add properties to >> >> objects their own libraries do not create. >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss > >
Thanks for that information. I wasn't yet sure how to handle it. A parallel question is this: Is there any particular reason that the private container itself shouldn't be mutable? Or more directly, is there a good reason for private fields to only be create-able at declaration time? So far, all the logic I've created hinges on the reference to [[DeclarationInfo]] (which keeps all the known private names). Since that container is created at declaration time, it's not unfeasible for private properties to be appended after the declaration. I'm not particularly fond of the idea, but I'm also trying not to let my own biases commit me to a decision.
Thanks for that information. I wasn't yet sure how to handle it. A parallel question is this: Is there any particular reason that the private container itself shouldn't be mutable? Or more directly, is there a good reason for private fields to only be create-able at declaration time? So far, all the logic I've created hinges on the reference to [[DeclarationInfo]] (which keeps all the known private names). Since that container is created at declaration time, it's not unfeasible for private properties to be appended after the declaration. I'm not particularly fond of the idea, but I'm also trying not to let my own biases commit me to a decision. On Tue, Jul 31, 2018 at 4:41 PM Jordan Harband <ljharb at gmail.com> wrote: > Note that builtins with internal slots, like Map, Set, and Promise, are > still mutable after being frozen - so if one is trying to model internal > slots with some kind of property stored on the object, then freezing *must* > have no effect on the ability to alter their contents. > > On Tue, Jul 31, 2018 at 2:34 PM, Isiah Meadows <isiahmeadows at gmail.com> > wrote: > >> If you go back a few months, what you're proposing is *very* similar, >> at least functionally, to my previous iteration of my proposal: >> >> https://github.com/isiahmeadows/private-symbol-proposal/blob/c5c9781d9e76123c92d8fbc83681fdd3a9b0b319/README.md >> >> My main problem was that trying to limit private properties to objects >> created within a scope got complicated in a hurry once you considered >> all the small details, and it just didn't seem simple anymore. It only >> got more complicated when you started getting into the logistics of >> integrating with modules. >> >> So I've considered the issue and explored it pretty thoroughly - I >> *really* don't want private data to be limited to classes (which I >> dislike), but I did also previously have the concern of trying to >> limit who could define properties where. >> >> I will point out that you can prevent arbitrary private extension by >> simply doing `Object.preventExtensions(object)`. Because properties >> defined using private symbols are otherwise just normal properties, >> they still have to go through the same access checks normal properties >> have to, like [[IsExtensible]]. The only other concrete difference is >> that proxy hooks don't fire when you do things with private symbols. >> >> ----- >> >> Isiah Meadows >> contact at isiahmeadows.com >> www.isiahmeadows.com >> >> >> On Tue, Jul 31, 2018 at 3:09 PM, Ranando King <kingmph at gmail.com> wrote: >> >> What use case are you referring to here? >> > >> > In the case of SymbolTree, the objects in use are external. >> > >> >> I think there’s been a misunderstanding. Everybody agrees that that’s a >> >> bad pattern. It’s not what the point of private symbols would be. It’s >> not a >> >> target use case. >> > >> > That certainly puts my mind at ease. >> > >> >> As Isiah said, “all of the examples here I've presented are for >> scenarios >> >> where the state is related to the factory that created the objects.” >> > >> > If the factory that creates the objects is the also the only thing >> trying to >> > store private information on those objects, then I understand you're >> only >> > looking for per-instance module-private data, possibly with the ability >> to >> > use common private names. If that's the case, then it really is just 2 >> > simple extensions of my proposal: >> > * allow a Symbol when used as a private or protected property name to >> > persist as the private Symbol name for the private instance field on >> each >> > object for which it is used. >> > * create an additional privilege level (internal) that places the new >> > field's name in the [[DeclarationInfo]] of the function containing the >> > declaration. >> > >> > The effect of using these 2 features together is that anything within >> the >> > same function as the declared Symbol will gain access to the internal >> field >> > of all objects using that Symbol as a field name. >> > >> > On Tue, Jul 31, 2018 at 1:36 PM Darien Valentine <valentinium at gmail.com >> > >> > wrote: >> >> >> >> > I'd say you've identified the common pattern, but that pattern >> itself is >> >> > a bad use case, and the use of private symbols as you have defined >> them >> >> > doesn't do anything to correct the technical issue. >> >> >> >> I think there’s been a misunderstanding. Everybody agrees that that’s a >> >> bad pattern. It’s not what the point of private symbols would be. It’s >> not a >> >> target use case. >> >> >> >> > Since you cannot stick new properties onto a non-extensible object, >> even >> >> > private symbols won't solve the problem with your use case. >> >> >> >> That appending private symbols to external objects which are frozen >> >> wouldn’t work doesn’t matter precisely because it’s not a target use >> case. >> >> That it doesn’t work reliably might even be considered a positive, >> since it >> >> discourages something we all seem to agree is not good practice. >> >> >> >> It’s also not related to private symbols; this is already how >> properties >> >> work, regardless of what kind of key they have. >> >> >> >> > The difference here is that in your use cases, library A is >> "sneakily" >> >> > storing information on object B. >> >> >> >> What use case are you referring to here? I can’t find any example in >> the >> >> previous posts that matches these descriptions. As Isiah said, “all of >> the >> >> examples here I've presented are for scenarios where the state is >> related to >> >> the factory that created the objects.” The same is true of my examples. >> >> Everybody’s on the same page regarding not wanting to add properties to >> >> objects their own libraries do not create. >> _______________________________________________ >> 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/20180801/faabfb9d/attachment-0001.html>
If you go back a few months, what you're proposing is very similar, at
least functionally, to my previous iteration of my proposal:
That functional similarity is intentional. After pouring over years worth of posts, I figured out what the vast majority of the proposal-class-fields detractors actually wanted: an elegant, easily recognized syntax for adding private members to objects.
My main problem was that trying to limit private properties to objects created
within a scope got complicated in a hurry once you considered all the small details, and it just didn't seem simple anymore.
I noticed that about your proposal too. I'm also pretty sure that Daniel E. and Kevin G. ran into the same issues back during the proposal-private-names days which is why the private names concept is just an implementation detail in their current proposal. My proposal is made less complicated by breaking the problem down into the 3 pieces required to make it all work:
- a record to store private data
- an array to hold references to the schema records of accessible private data
- a schema record for the sharable data.
In this way private = encapsulated on a non-function, protected = private + shared, and static = encapsulated on a function. It should be easy to sort out how the data would be stored given such simple definitions. These simple definitions also mean that encapsulation is naturally confined to definitions. Attempts to alter that state lead to strange logical contradictions and potential leaks of encapsulated data. I have thought of the possibility that private data could be added after definition, but every attempt I make to consider such a thing has so far led to a risk of leaking.
I've been working on some code that can serve as a proof-of-concept in ES6. It will implement all of my proposal that can reasonably be implemented in ES6 using Proxy. It's already in the proposal repository under the POC branch, but it's still a WIP. For now, it already supports inheriting from native objects. I'm working on subclassing right now. By the time I get done (likely this coming Monday), it should support every feature in my proposal. I'm basically using it as a means to check the viability of my proposal.
> If you go back a few months, what you're proposing is *very* similar, at least functionally, to my previous iteration of my proposal: That functional similarity is intentional. After pouring over years worth of posts, I figured out what the vast majority of the proposal-class-fields detractors actually wanted: an elegant, easily recognized syntax for adding private members to objects. > My main problem was that trying to limit private properties to objects created within a scope got complicated in a hurry once you considered all the small details, and it just didn't seem simple anymore. I noticed that about your proposal too. I'm also pretty sure that Daniel E. and Kevin G. ran into the same issues back during the proposal-private-names days which is why the private names concept is just an implementation detail in their current proposal. My proposal is made less complicated by breaking the problem down into the 3 pieces required to make it all work: 1. a record to store private data 2. an array to hold references to the schema records of accessible private data 3. a schema record for the sharable data. In this way private = encapsulated on a non-function, protected = private + shared, and static = encapsulated on a function. It should be easy to sort out how the data would be stored given such simple definitions. These simple definitions also mean that encapsulation is naturally confined to definitions. Attempts to alter that state lead to strange logical contradictions and potential leaks of encapsulated data. I have thought of the possibility that private data could be added after definition, but every attempt I make to consider such a thing has so far led to a risk of leaking. I've been working on some code that can serve as a proof-of-concept in ES6. It will implement all of my proposal that can reasonably be implemented in ES6 using Proxy. It's already in the proposal repository under the POC branch, but it's still a WIP. For now, it already supports inheriting from native objects. I'm working on subclassing right now. By the time I get done (likely this coming Monday), it should support every feature in my proposal. I'm basically using it as a means to check the viability of my proposal. On Tue, Jul 31, 2018 at 4:35 PM Isiah Meadows <isiahmeadows at gmail.com> wrote: > If you go back a few months, what you're proposing is *very* similar, > at least functionally, to my previous iteration of my proposal: > > https://github.com/isiahmeadows/private-symbol-proposal/blob/c5c9781d9e76123c92d8fbc83681fdd3a9b0b319/README.md > > My main problem was that trying to limit private properties to objects > created within a scope got complicated in a hurry once you considered > all the small details, and it just didn't seem simple anymore. It only > got more complicated when you started getting into the logistics of > integrating with modules. > > So I've considered the issue and explored it pretty thoroughly - I > *really* don't want private data to be limited to classes (which I > dislike), but I did also previously have the concern of trying to > limit who could define properties where. > > I will point out that you can prevent arbitrary private extension by > simply doing `Object.preventExtensions(object)`. Because properties > defined using private symbols are otherwise just normal properties, > they still have to go through the same access checks normal properties > have to, like [[IsExtensible]]. The only other concrete difference is > that proxy hooks don't fire when you do things with private symbols. > > ----- > > Isiah Meadows > contact at isiahmeadows.com > www.isiahmeadows.com > > > On Tue, Jul 31, 2018 at 3:09 PM, Ranando King <kingmph at gmail.com> wrote: > >> What use case are you referring to here? > > > > In the case of SymbolTree, the objects in use are external. > > > >> I think there’s been a misunderstanding. Everybody agrees that that’s a > >> bad pattern. It’s not what the point of private symbols would be. It’s > not a > >> target use case. > > > > That certainly puts my mind at ease. > > > >> As Isiah said, “all of the examples here I've presented are for > scenarios > >> where the state is related to the factory that created the objects.” > > > > If the factory that creates the objects is the also the only thing > trying to > > store private information on those objects, then I understand you're only > > looking for per-instance module-private data, possibly with the ability > to > > use common private names. If that's the case, then it really is just 2 > > simple extensions of my proposal: > > * allow a Symbol when used as a private or protected property name to > > persist as the private Symbol name for the private instance field on each > > object for which it is used. > > * create an additional privilege level (internal) that places the new > > field's name in the [[DeclarationInfo]] of the function containing the > > declaration. > > > > The effect of using these 2 features together is that anything within the > > same function as the declared Symbol will gain access to the internal > field > > of all objects using that Symbol as a field name. > > > > On Tue, Jul 31, 2018 at 1:36 PM Darien Valentine <valentinium at gmail.com> > > wrote: > >> > >> > I'd say you've identified the common pattern, but that pattern itself > is > >> > a bad use case, and the use of private symbols as you have defined > them > >> > doesn't do anything to correct the technical issue. > >> > >> I think there’s been a misunderstanding. Everybody agrees that that’s a > >> bad pattern. It’s not what the point of private symbols would be. It’s > not a > >> target use case. > >> > >> > Since you cannot stick new properties onto a non-extensible object, > even > >> > private symbols won't solve the problem with your use case. > >> > >> That appending private symbols to external objects which are frozen > >> wouldn’t work doesn’t matter precisely because it’s not a target use > case. > >> That it doesn’t work reliably might even be considered a positive, > since it > >> discourages something we all seem to agree is not good practice. > >> > >> It’s also not related to private symbols; this is already how properties > >> work, regardless of what kind of key they have. > >> > >> > The difference here is that in your use cases, library A is "sneakily" > >> > storing information on object B. > >> > >> What use case are you referring to here? I can’t find any example in the > >> previous posts that matches these descriptions. As Isiah said, “all of > the > >> examples here I've presented are for scenarios where the state is > related to > >> the factory that created the objects.” The same is true of my examples. > >> Everybody’s on the same page regarding not wanting to add properties to > >> objects their own libraries do not create. > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180801/1897796c/attachment-0001.html>
Do you have a link to this proposal so I can take a look at it? It'd be much easier to critique it if I could see the proposal text.
Isiah Meadows contact at isiahmeadows.com, www.isiahmeadows.com
Do you have a link to this proposal so I can take a look at it? It'd be much easier to critique it if I could see the proposal text. ----- Isiah Meadows contact at isiahmeadows.com www.isiahmeadows.com On Wed, Aug 1, 2018 at 2:18 AM, Ranando King <kingmph at gmail.com> wrote: >> If you go back a few months, what you're proposing is *very* similar, at >> least functionally, to my previous iteration of my proposal: > > That functional similarity is intentional. After pouring over years worth of > posts, I figured out what the vast majority of the proposal-class-fields > detractors actually wanted: an elegant, easily recognized syntax for adding > private members to objects. > >> My main problem was that trying to limit private properties to objects >> created within a scope got complicated in a hurry once you considered all >> the small details, and it just didn't seem simple anymore. > > I noticed that about your proposal too. I'm also pretty sure that Daniel E. > and Kevin G. ran into the same issues back during the proposal-private-names > days which is why the private names concept is just an implementation detail > in their current proposal. My proposal is made less complicated by breaking > the problem down into the 3 pieces required to make it all work: > 1. a record to store private data > 2. an array to hold references to the schema records of accessible private > data > 3. a schema record for the sharable data. > > In this way private = encapsulated on a non-function, protected = private + > shared, and static = encapsulated on a function. It should be easy to sort > out how the data would be stored given such simple definitions. These simple > definitions also mean that encapsulation is naturally confined to > definitions. Attempts to alter that state lead to strange logical > contradictions and potential leaks of encapsulated data. I have thought of > the possibility that private data could be added after definition, but every > attempt I make to consider such a thing has so far led to a risk of leaking. > > I've been working on some code that can serve as a proof-of-concept in ES6. > It will implement all of my proposal that can reasonably be implemented in > ES6 using Proxy. It's already in the proposal repository under the POC > branch, but it's still a WIP. For now, it already supports inheriting from > native objects. I'm working on subclassing right now. By the time I get done > (likely this coming Monday), it should support every feature in my proposal. > I'm basically using it as a means to check the viability of my proposal. > > On Tue, Jul 31, 2018 at 4:35 PM Isiah Meadows <isiahmeadows at gmail.com> > wrote: >> >> If you go back a few months, what you're proposing is *very* similar, >> at least functionally, to my previous iteration of my proposal: >> >> https://github.com/isiahmeadows/private-symbol-proposal/blob/c5c9781d9e76123c92d8fbc83681fdd3a9b0b319/README.md >> >> My main problem was that trying to limit private properties to objects >> created within a scope got complicated in a hurry once you considered >> all the small details, and it just didn't seem simple anymore. It only >> got more complicated when you started getting into the logistics of >> integrating with modules. >> >> So I've considered the issue and explored it pretty thoroughly - I >> *really* don't want private data to be limited to classes (which I >> dislike), but I did also previously have the concern of trying to >> limit who could define properties where. >> >> I will point out that you can prevent arbitrary private extension by >> simply doing `Object.preventExtensions(object)`. Because properties >> defined using private symbols are otherwise just normal properties, >> they still have to go through the same access checks normal properties >> have to, like [[IsExtensible]]. The only other concrete difference is >> that proxy hooks don't fire when you do things with private symbols. >> >> ----- >> >> Isiah Meadows >> contact at isiahmeadows.com >> www.isiahmeadows.com >> >> >> On Tue, Jul 31, 2018 at 3:09 PM, Ranando King <kingmph at gmail.com> wrote: >> >> What use case are you referring to here? >> > >> > In the case of SymbolTree, the objects in use are external. >> > >> >> I think there’s been a misunderstanding. Everybody agrees that that’s a >> >> bad pattern. It’s not what the point of private symbols would be. It’s >> >> not a >> >> target use case. >> > >> > That certainly puts my mind at ease. >> > >> >> As Isiah said, “all of the examples here I've presented are for >> >> scenarios >> >> where the state is related to the factory that created the objects.” >> > >> > If the factory that creates the objects is the also the only thing >> > trying to >> > store private information on those objects, then I understand you're >> > only >> > looking for per-instance module-private data, possibly with the ability >> > to >> > use common private names. If that's the case, then it really is just 2 >> > simple extensions of my proposal: >> > * allow a Symbol when used as a private or protected property name to >> > persist as the private Symbol name for the private instance field on >> > each >> > object for which it is used. >> > * create an additional privilege level (internal) that places the new >> > field's name in the [[DeclarationInfo]] of the function containing the >> > declaration. >> > >> > The effect of using these 2 features together is that anything within >> > the >> > same function as the declared Symbol will gain access to the internal >> > field >> > of all objects using that Symbol as a field name. >> > >> > On Tue, Jul 31, 2018 at 1:36 PM Darien Valentine <valentinium at gmail.com> >> > wrote: >> >> >> >> > I'd say you've identified the common pattern, but that pattern itself >> >> > is >> >> > a bad use case, and the use of private symbols as you have defined >> >> > them >> >> > doesn't do anything to correct the technical issue. >> >> >> >> I think there’s been a misunderstanding. Everybody agrees that that’s a >> >> bad pattern. It’s not what the point of private symbols would be. It’s >> >> not a >> >> target use case. >> >> >> >> > Since you cannot stick new properties onto a non-extensible object, >> >> > even >> >> > private symbols won't solve the problem with your use case. >> >> >> >> That appending private symbols to external objects which are frozen >> >> wouldn’t work doesn’t matter precisely because it’s not a target use >> >> case. >> >> That it doesn’t work reliably might even be considered a positive, >> >> since it >> >> discourages something we all seem to agree is not good practice. >> >> >> >> It’s also not related to private symbols; this is already how >> >> properties >> >> work, regardless of what kind of key they have. >> >> >> >> > The difference here is that in your use cases, library A is >> >> > "sneakily" >> >> > storing information on object B. >> >> >> >> What use case are you referring to here? I can’t find any example in >> >> the >> >> previous posts that matches these descriptions. As Isiah said, “all of >> >> the >> >> examples here I've presented are for scenarios where the state is >> >> related to >> >> the factory that created the objects.” The same is true of my examples. >> >> Everybody’s on the same page regarding not wanting to add properties to >> >> objects their own libraries do not create.
https://github.com/rdking/proposal-object-members/blob/master/README.md On Wed, Aug 1, 2018 at 2:01 AM Isiah Meadows <isiahmeadows at gmail.com> wrote: > Do you have a link to this proposal so I can take a look at it? It'd > be much easier to critique it if I could see the proposal text. > ----- > > Isiah Meadows > contact at isiahmeadows.com > www.isiahmeadows.com > > > On Wed, Aug 1, 2018 at 2:18 AM, Ranando King <kingmph at gmail.com> wrote: > >> If you go back a few months, what you're proposing is *very* similar, at > >> least functionally, to my previous iteration of my proposal: > > > > That functional similarity is intentional. After pouring over years > worth of > > posts, I figured out what the vast majority of the proposal-class-fields > > detractors actually wanted: an elegant, easily recognized syntax for > adding > > private members to objects. > > > >> My main problem was that trying to limit private properties to objects > >> created within a scope got complicated in a hurry once you considered > all > >> the small details, and it just didn't seem simple anymore. > > > > I noticed that about your proposal too. I'm also pretty sure that Daniel > E. > > and Kevin G. ran into the same issues back during the > proposal-private-names > > days which is why the private names concept is just an implementation > detail > > in their current proposal. My proposal is made less complicated by > breaking > > the problem down into the 3 pieces required to make it all work: > > 1. a record to store private data > > 2. an array to hold references to the schema records of accessible > private > > data > > 3. a schema record for the sharable data. > > > > In this way private = encapsulated on a non-function, protected = > private + > > shared, and static = encapsulated on a function. It should be easy to > sort > > out how the data would be stored given such simple definitions. These > simple > > definitions also mean that encapsulation is naturally confined to > > definitions. Attempts to alter that state lead to strange logical > > contradictions and potential leaks of encapsulated data. I have thought > of > > the possibility that private data could be added after definition, but > every > > attempt I make to consider such a thing has so far led to a risk of > leaking. > > > > I've been working on some code that can serve as a proof-of-concept in > ES6. > > It will implement all of my proposal that can reasonably be implemented > in > > ES6 using Proxy. It's already in the proposal repository under the POC > > branch, but it's still a WIP. For now, it already supports inheriting > from > > native objects. I'm working on subclassing right now. By the time I get > done > > (likely this coming Monday), it should support every feature in my > proposal. > > I'm basically using it as a means to check the viability of my proposal. > > > > On Tue, Jul 31, 2018 at 4:35 PM Isiah Meadows <isiahmeadows at gmail.com> > > wrote: > >> > >> If you go back a few months, what you're proposing is *very* similar, > >> at least functionally, to my previous iteration of my proposal: > >> > >> > https://github.com/isiahmeadows/private-symbol-proposal/blob/c5c9781d9e76123c92d8fbc83681fdd3a9b0b319/README.md > >> > >> My main problem was that trying to limit private properties to objects > >> created within a scope got complicated in a hurry once you considered > >> all the small details, and it just didn't seem simple anymore. It only > >> got more complicated when you started getting into the logistics of > >> integrating with modules. > >> > >> So I've considered the issue and explored it pretty thoroughly - I > >> *really* don't want private data to be limited to classes (which I > >> dislike), but I did also previously have the concern of trying to > >> limit who could define properties where. > >> > >> I will point out that you can prevent arbitrary private extension by > >> simply doing `Object.preventExtensions(object)`. Because properties > >> defined using private symbols are otherwise just normal properties, > >> they still have to go through the same access checks normal properties > >> have to, like [[IsExtensible]]. The only other concrete difference is > >> that proxy hooks don't fire when you do things with private symbols. > >> > >> ----- > >> > >> Isiah Meadows > >> contact at isiahmeadows.com > >> www.isiahmeadows.com > >> > >> > >> On Tue, Jul 31, 2018 at 3:09 PM, Ranando King <kingmph at gmail.com> > wrote: > >> >> What use case are you referring to here? > >> > > >> > In the case of SymbolTree, the objects in use are external. > >> > > >> >> I think there’s been a misunderstanding. Everybody agrees that > that’s a > >> >> bad pattern. It’s not what the point of private symbols would be. > It’s > >> >> not a > >> >> target use case. > >> > > >> > That certainly puts my mind at ease. > >> > > >> >> As Isiah said, “all of the examples here I've presented are for > >> >> scenarios > >> >> where the state is related to the factory that created the objects.” > >> > > >> > If the factory that creates the objects is the also the only thing > >> > trying to > >> > store private information on those objects, then I understand you're > >> > only > >> > looking for per-instance module-private data, possibly with the > ability > >> > to > >> > use common private names. If that's the case, then it really is just 2 > >> > simple extensions of my proposal: > >> > * allow a Symbol when used as a private or protected property name to > >> > persist as the private Symbol name for the private instance field on > >> > each > >> > object for which it is used. > >> > * create an additional privilege level (internal) that places the new > >> > field's name in the [[DeclarationInfo]] of the function containing the > >> > declaration. > >> > > >> > The effect of using these 2 features together is that anything within > >> > the > >> > same function as the declared Symbol will gain access to the internal > >> > field > >> > of all objects using that Symbol as a field name. > >> > > >> > On Tue, Jul 31, 2018 at 1:36 PM Darien Valentine < > valentinium at gmail.com> > >> > wrote: > >> >> > >> >> > I'd say you've identified the common pattern, but that pattern > itself > >> >> > is > >> >> > a bad use case, and the use of private symbols as you have defined > >> >> > them > >> >> > doesn't do anything to correct the technical issue. > >> >> > >> >> I think there’s been a misunderstanding. Everybody agrees that > that’s a > >> >> bad pattern. It’s not what the point of private symbols would be. > It’s > >> >> not a > >> >> target use case. > >> >> > >> >> > Since you cannot stick new properties onto a non-extensible object, > >> >> > even > >> >> > private symbols won't solve the problem with your use case. > >> >> > >> >> That appending private symbols to external objects which are frozen > >> >> wouldn’t work doesn’t matter precisely because it’s not a target use > >> >> case. > >> >> That it doesn’t work reliably might even be considered a positive, > >> >> since it > >> >> discourages something we all seem to agree is not good practice. > >> >> > >> >> It’s also not related to private symbols; this is already how > >> >> properties > >> >> work, regardless of what kind of key they have. > >> >> > >> >> > The difference here is that in your use cases, library A is > >> >> > "sneakily" > >> >> > storing information on object B. > >> >> > >> >> What use case are you referring to here? I can’t find any example in > >> >> the > >> >> previous posts that matches these descriptions. As Isiah said, “all > of > >> >> the > >> >> examples here I've presented are for scenarios where the state is > >> >> related to > >> >> the factory that created the objects.” The same is true of my > examples. > >> >> Everybody’s on the same page regarding not wanting to add properties > to > >> >> objects their own libraries do not create. > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180801/db00c967/attachment.html>
Okay, now that I look at that proposal, I see two issues right off:
- It's super incredibly boilerplatey and verbose syntactically. I don't know very many people who'd be willing to downgrade very far from even what TypeScript has. (I'm specifically referring to the declarations here.)
protected
on an object literal is next to useless. I've used that kind of feature almost never.
I also find it odd you're supporting private dynamic properties. It does make polyfilling next to impossible, though.
Just my 2 cents on it. (I glanced over this while very tired, so I probably missed several highlights. These are what stuck out to me.)
Isiah Meadows contact at isiahmeadows.com, www.isiahmeadows.com
Okay, now that I look at that proposal, I see two issues right off: 1. It's *super incredibly boilerplatey* and verbose syntactically. I don't know very many people who'd be willing to downgrade very far from even what TypeScript has. (I'm specifically referring to the declarations here.) 2. `protected` on an object literal is next to useless. I've used that kind of feature almost never. I also find it odd you're supporting private dynamic properties. It does make polyfilling next to impossible, though. Just my 2 cents on it. (I glanced over this while very tired, so I probably missed several highlights. These are what stuck out to me.) ----- Isiah Meadows contact at isiahmeadows.com www.isiahmeadows.com On Wed, Aug 1, 2018 at 11:54 PM, Ranando King <kingmph at gmail.com> wrote: > https://github.com/rdking/proposal-object-members/blob/master/README.md > > On Wed, Aug 1, 2018 at 2:01 AM Isiah Meadows <isiahmeadows at gmail.com> wrote: >> >> Do you have a link to this proposal so I can take a look at it? It'd >> be much easier to critique it if I could see the proposal text. >> ----- >> >> Isiah Meadows >> contact at isiahmeadows.com >> www.isiahmeadows.com >> >> >> On Wed, Aug 1, 2018 at 2:18 AM, Ranando King <kingmph at gmail.com> wrote: >> >> If you go back a few months, what you're proposing is *very* similar, >> >> at >> >> least functionally, to my previous iteration of my proposal: >> > >> > That functional similarity is intentional. After pouring over years >> > worth of >> > posts, I figured out what the vast majority of the proposal-class-fields >> > detractors actually wanted: an elegant, easily recognized syntax for >> > adding >> > private members to objects. >> > >> >> My main problem was that trying to limit private properties to objects >> >> created within a scope got complicated in a hurry once you considered >> >> all >> >> the small details, and it just didn't seem simple anymore. >> > >> > I noticed that about your proposal too. I'm also pretty sure that Daniel >> > E. >> > and Kevin G. ran into the same issues back during the >> > proposal-private-names >> > days which is why the private names concept is just an implementation >> > detail >> > in their current proposal. My proposal is made less complicated by >> > breaking >> > the problem down into the 3 pieces required to make it all work: >> > 1. a record to store private data >> > 2. an array to hold references to the schema records of accessible >> > private >> > data >> > 3. a schema record for the sharable data. >> > >> > In this way private = encapsulated on a non-function, protected = >> > private + >> > shared, and static = encapsulated on a function. It should be easy to >> > sort >> > out how the data would be stored given such simple definitions. These >> > simple >> > definitions also mean that encapsulation is naturally confined to >> > definitions. Attempts to alter that state lead to strange logical >> > contradictions and potential leaks of encapsulated data. I have thought >> > of >> > the possibility that private data could be added after definition, but >> > every >> > attempt I make to consider such a thing has so far led to a risk of >> > leaking. >> > >> > I've been working on some code that can serve as a proof-of-concept in >> > ES6. >> > It will implement all of my proposal that can reasonably be implemented >> > in >> > ES6 using Proxy. It's already in the proposal repository under the POC >> > branch, but it's still a WIP. For now, it already supports inheriting >> > from >> > native objects. I'm working on subclassing right now. By the time I get >> > done >> > (likely this coming Monday), it should support every feature in my >> > proposal. >> > I'm basically using it as a means to check the viability of my proposal. >> > >> > On Tue, Jul 31, 2018 at 4:35 PM Isiah Meadows <isiahmeadows at gmail.com> >> > wrote: >> >> >> >> If you go back a few months, what you're proposing is *very* similar, >> >> at least functionally, to my previous iteration of my proposal: >> >> >> >> >> >> https://github.com/isiahmeadows/private-symbol-proposal/blob/c5c9781d9e76123c92d8fbc83681fdd3a9b0b319/README.md >> >> >> >> My main problem was that trying to limit private properties to objects >> >> created within a scope got complicated in a hurry once you considered >> >> all the small details, and it just didn't seem simple anymore. It only >> >> got more complicated when you started getting into the logistics of >> >> integrating with modules. >> >> >> >> So I've considered the issue and explored it pretty thoroughly - I >> >> *really* don't want private data to be limited to classes (which I >> >> dislike), but I did also previously have the concern of trying to >> >> limit who could define properties where. >> >> >> >> I will point out that you can prevent arbitrary private extension by >> >> simply doing `Object.preventExtensions(object)`. Because properties >> >> defined using private symbols are otherwise just normal properties, >> >> they still have to go through the same access checks normal properties >> >> have to, like [[IsExtensible]]. The only other concrete difference is >> >> that proxy hooks don't fire when you do things with private symbols. >> >> >> >> ----- >> >> >> >> Isiah Meadows >> >> contact at isiahmeadows.com >> >> www.isiahmeadows.com >> >> >> >> >> >> On Tue, Jul 31, 2018 at 3:09 PM, Ranando King <kingmph at gmail.com> >> >> wrote: >> >> >> What use case are you referring to here? >> >> > >> >> > In the case of SymbolTree, the objects in use are external. >> >> > >> >> >> I think there’s been a misunderstanding. Everybody agrees that >> >> >> that’s a >> >> >> bad pattern. It’s not what the point of private symbols would be. >> >> >> It’s >> >> >> not a >> >> >> target use case. >> >> > >> >> > That certainly puts my mind at ease. >> >> > >> >> >> As Isiah said, “all of the examples here I've presented are for >> >> >> scenarios >> >> >> where the state is related to the factory that created the objects.” >> >> > >> >> > If the factory that creates the objects is the also the only thing >> >> > trying to >> >> > store private information on those objects, then I understand you're >> >> > only >> >> > looking for per-instance module-private data, possibly with the >> >> > ability >> >> > to >> >> > use common private names. If that's the case, then it really is just >> >> > 2 >> >> > simple extensions of my proposal: >> >> > * allow a Symbol when used as a private or protected property name to >> >> > persist as the private Symbol name for the private instance field on >> >> > each >> >> > object for which it is used. >> >> > * create an additional privilege level (internal) that places the new >> >> > field's name in the [[DeclarationInfo]] of the function containing >> >> > the >> >> > declaration. >> >> > >> >> > The effect of using these 2 features together is that anything within >> >> > the >> >> > same function as the declared Symbol will gain access to the internal >> >> > field >> >> > of all objects using that Symbol as a field name. >> >> > >> >> > On Tue, Jul 31, 2018 at 1:36 PM Darien Valentine >> >> > <valentinium at gmail.com> >> >> > wrote: >> >> >> >> >> >> > I'd say you've identified the common pattern, but that pattern >> >> >> > itself >> >> >> > is >> >> >> > a bad use case, and the use of private symbols as you have defined >> >> >> > them >> >> >> > doesn't do anything to correct the technical issue. >> >> >> >> >> >> I think there’s been a misunderstanding. Everybody agrees that >> >> >> that’s a >> >> >> bad pattern. It’s not what the point of private symbols would be. >> >> >> It’s >> >> >> not a >> >> >> target use case. >> >> >> >> >> >> > Since you cannot stick new properties onto a non-extensible >> >> >> > object, >> >> >> > even >> >> >> > private symbols won't solve the problem with your use case. >> >> >> >> >> >> That appending private symbols to external objects which are frozen >> >> >> wouldn’t work doesn’t matter precisely because it’s not a target use >> >> >> case. >> >> >> That it doesn’t work reliably might even be considered a positive, >> >> >> since it >> >> >> discourages something we all seem to agree is not good practice. >> >> >> >> >> >> It’s also not related to private symbols; this is already how >> >> >> properties >> >> >> work, regardless of what kind of key they have. >> >> >> >> >> >> > The difference here is that in your use cases, library A is >> >> >> > "sneakily" >> >> >> > storing information on object B. >> >> >> >> >> >> What use case are you referring to here? I can’t find any example in >> >> >> the >> >> >> previous posts that matches these descriptions. As Isiah said, “all >> >> >> of >> >> >> the >> >> >> examples here I've presented are for scenarios where the state is >> >> >> related to >> >> >> the factory that created the objects.” The same is true of my >> >> >> examples. >> >> >> Everybody’s on the same page regarding not wanting to add properties >> >> >> to >> >> >> objects their own libraries do not create.
If I understand the terminology, "private dynamic properties" are easily polyfilled via weakmaps?
I actually think it's odd there is no attempt to implement dynamic properties in the other "private properties" proposals.
If I understand the terminology, "private dynamic properties" are easily polyfilled via weakmaps? I actually think it's odd there is no attempt to implement dynamic properties in the other "private properties" proposals. On Friday, August 3, 2018, Isiah Meadows <isiahmeadows at gmail.com> wrote: > Okay, now that I look at that proposal, I see two issues right off: > > 1. It's *super incredibly boilerplatey* and verbose syntactically. I > don't know very many people who'd be willing to downgrade very far > from even what TypeScript has. (I'm specifically referring to the > declarations here.) > 2. `protected` on an object literal is next to useless. I've used that > kind of feature almost never. > > I also find it odd you're supporting private dynamic properties. It > does make polyfilling next to impossible, though. > > Just my 2 cents on it. (I glanced over this while very tired, so I > probably missed several highlights. These are what stuck out to me.) > > ----- > > Isiah Meadows > contact at isiahmeadows.com > www.isiahmeadows.com > > > On Wed, Aug 1, 2018 at 11:54 PM, Ranando King <kingmph at gmail.com> wrote: > > https://github.com/rdking/proposal-object-members/blob/master/README.md > > > > On Wed, Aug 1, 2018 at 2:01 AM Isiah Meadows <isiahmeadows at gmail.com> > wrote: > >> > >> Do you have a link to this proposal so I can take a look at it? It'd > >> be much easier to critique it if I could see the proposal text. > >> ----- > >> > >> Isiah Meadows > >> contact at isiahmeadows.com > >> www.isiahmeadows.com > >> > >> > >> On Wed, Aug 1, 2018 at 2:18 AM, Ranando King <kingmph at gmail.com> wrote: > >> >> If you go back a few months, what you're proposing is *very* similar, > >> >> at > >> >> least functionally, to my previous iteration of my proposal: > >> > > >> > That functional similarity is intentional. After pouring over years > >> > worth of > >> > posts, I figured out what the vast majority of the > proposal-class-fields > >> > detractors actually wanted: an elegant, easily recognized syntax for > >> > adding > >> > private members to objects. > >> > > >> >> My main problem was that trying to limit private properties to > objects > >> >> created within a scope got complicated in a hurry once you considered > >> >> all > >> >> the small details, and it just didn't seem simple anymore. > >> > > >> > I noticed that about your proposal too. I'm also pretty sure that > Daniel > >> > E. > >> > and Kevin G. ran into the same issues back during the > >> > proposal-private-names > >> > days which is why the private names concept is just an implementation > >> > detail > >> > in their current proposal. My proposal is made less complicated by > >> > breaking > >> > the problem down into the 3 pieces required to make it all work: > >> > 1. a record to store private data > >> > 2. an array to hold references to the schema records of accessible > >> > private > >> > data > >> > 3. a schema record for the sharable data. > >> > > >> > In this way private = encapsulated on a non-function, protected = > >> > private + > >> > shared, and static = encapsulated on a function. It should be easy to > >> > sort > >> > out how the data would be stored given such simple definitions. These > >> > simple > >> > definitions also mean that encapsulation is naturally confined to > >> > definitions. Attempts to alter that state lead to strange logical > >> > contradictions and potential leaks of encapsulated data. I have > thought > >> > of > >> > the possibility that private data could be added after definition, but > >> > every > >> > attempt I make to consider such a thing has so far led to a risk of > >> > leaking. > >> > > >> > I've been working on some code that can serve as a proof-of-concept in > >> > ES6. > >> > It will implement all of my proposal that can reasonably be > implemented > >> > in > >> > ES6 using Proxy. It's already in the proposal repository under the POC > >> > branch, but it's still a WIP. For now, it already supports inheriting > >> > from > >> > native objects. I'm working on subclassing right now. By the time I > get > >> > done > >> > (likely this coming Monday), it should support every feature in my > >> > proposal. > >> > I'm basically using it as a means to check the viability of my > proposal. > >> > > >> > On Tue, Jul 31, 2018 at 4:35 PM Isiah Meadows <isiahmeadows at gmail.com > > > >> > wrote: > >> >> > >> >> If you go back a few months, what you're proposing is *very* similar, > >> >> at least functionally, to my previous iteration of my proposal: > >> >> > >> >> > >> >> https://github.com/isiahmeadows/private-symbol-proposal/blob/ > c5c9781d9e76123c92d8fbc83681fdd3a9b0b319/README.md > >> >> > >> >> My main problem was that trying to limit private properties to > objects > >> >> created within a scope got complicated in a hurry once you considered > >> >> all the small details, and it just didn't seem simple anymore. It > only > >> >> got more complicated when you started getting into the logistics of > >> >> integrating with modules. > >> >> > >> >> So I've considered the issue and explored it pretty thoroughly - I > >> >> *really* don't want private data to be limited to classes (which I > >> >> dislike), but I did also previously have the concern of trying to > >> >> limit who could define properties where. > >> >> > >> >> I will point out that you can prevent arbitrary private extension by > >> >> simply doing `Object.preventExtensions(object)`. Because properties > >> >> defined using private symbols are otherwise just normal properties, > >> >> they still have to go through the same access checks normal > properties > >> >> have to, like [[IsExtensible]]. The only other concrete difference is > >> >> that proxy hooks don't fire when you do things with private symbols. > >> >> > >> >> ----- > >> >> > >> >> Isiah Meadows > >> >> contact at isiahmeadows.com > >> >> www.isiahmeadows.com > >> >> > >> >> > >> >> On Tue, Jul 31, 2018 at 3:09 PM, Ranando King <kingmph at gmail.com> > >> >> wrote: > >> >> >> What use case are you referring to here? > >> >> > > >> >> > In the case of SymbolTree, the objects in use are external. > >> >> > > >> >> >> I think there’s been a misunderstanding. Everybody agrees that > >> >> >> that’s a > >> >> >> bad pattern. It’s not what the point of private symbols would be. > >> >> >> It’s > >> >> >> not a > >> >> >> target use case. > >> >> > > >> >> > That certainly puts my mind at ease. > >> >> > > >> >> >> As Isiah said, “all of the examples here I've presented are for > >> >> >> scenarios > >> >> >> where the state is related to the factory that created the > objects.” > >> >> > > >> >> > If the factory that creates the objects is the also the only thing > >> >> > trying to > >> >> > store private information on those objects, then I understand > you're > >> >> > only > >> >> > looking for per-instance module-private data, possibly with the > >> >> > ability > >> >> > to > >> >> > use common private names. If that's the case, then it really is > just > >> >> > 2 > >> >> > simple extensions of my proposal: > >> >> > * allow a Symbol when used as a private or protected property name > to > >> >> > persist as the private Symbol name for the private instance field > on > >> >> > each > >> >> > object for which it is used. > >> >> > * create an additional privilege level (internal) that places the > new > >> >> > field's name in the [[DeclarationInfo]] of the function containing > >> >> > the > >> >> > declaration. > >> >> > > >> >> > The effect of using these 2 features together is that anything > within > >> >> > the > >> >> > same function as the declared Symbol will gain access to the > internal > >> >> > field > >> >> > of all objects using that Symbol as a field name. > >> >> > > >> >> > On Tue, Jul 31, 2018 at 1:36 PM Darien Valentine > >> >> > <valentinium at gmail.com> > >> >> > wrote: > >> >> >> > >> >> >> > I'd say you've identified the common pattern, but that pattern > >> >> >> > itself > >> >> >> > is > >> >> >> > a bad use case, and the use of private symbols as you have > defined > >> >> >> > them > >> >> >> > doesn't do anything to correct the technical issue. > >> >> >> > >> >> >> I think there’s been a misunderstanding. Everybody agrees that > >> >> >> that’s a > >> >> >> bad pattern. It’s not what the point of private symbols would be. > >> >> >> It’s > >> >> >> not a > >> >> >> target use case. > >> >> >> > >> >> >> > Since you cannot stick new properties onto a non-extensible > >> >> >> > object, > >> >> >> > even > >> >> >> > private symbols won't solve the problem with your use case. > >> >> >> > >> >> >> That appending private symbols to external objects which are > frozen > >> >> >> wouldn’t work doesn’t matter precisely because it’s not a target > use > >> >> >> case. > >> >> >> That it doesn’t work reliably might even be considered a positive, > >> >> >> since it > >> >> >> discourages something we all seem to agree is not good practice. > >> >> >> > >> >> >> It’s also not related to private symbols; this is already how > >> >> >> properties > >> >> >> work, regardless of what kind of key they have. > >> >> >> > >> >> >> > The difference here is that in your use cases, library A is > >> >> >> > "sneakily" > >> >> >> > storing information on object B. > >> >> >> > >> >> >> What use case are you referring to here? I can’t find any example > in > >> >> >> the > >> >> >> previous posts that matches these descriptions. As Isiah said, > “all > >> >> >> of > >> >> >> the > >> >> >> examples here I've presented are for scenarios where the state is > >> >> >> related to > >> >> >> the factory that created the objects.” The same is true of my > >> >> >> examples. > >> >> >> Everybody’s on the same page regarding not wanting to add > properties > >> >> >> to > >> >> >> objects their own libraries do not create. > _______________________________________________ > 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/20180803/d485c684/attachment-0001.html>
- It's super incredibly boilerplatey and verbose syntactically.
I'm not sure what you mean by "boilerplatey". As for being verbose, I'm just using the keywords everyone understands for this purpose. IMO, there's no advantage in trying to find some shorthand to do the same thing just because it saves a keystroke or two when it makes the code significantly more difficult to understand.
protected
on an object literal is next to useless. I've used that kind
of feature almost never.
I get where you're coming from with that. I don't see it being used very
often (kinda like with
), but it has to be there. If someone wants to use
the facilities of class
without the limitations of the keyword, and the
intent is to build vertical hierarchies, they'll need the "protected"
keyword on their prototype definition to share private data with descendant
factories. It's even more necessary for people writing factory factories.
The only other way to achieve the same thing would be to force them to use
Function()
or eval
and build up the code as strings. I'd rather avoid
that.
I also find it odd you're supporting private dynamic properties.
How'd you get to the idea that I'm supporting dynamic private properties? The first 2 paragraphs in the implementation say that all private container records are sealed, and all fields in info records are added read-only. If it wasn't clear from that, I'm going to have to re-write that section. However, the intent is that after the declaration process is complete, what you have is all you can get. No additional private fields can be added later. I considered dynamic private data, but that can get very messy very quickly.
I actually think it's odd there is no attempt to implement dynamic
properties in the other "private properties" proposals.
It's not that odd. There are issues around inheritance when a subclass can
remove the protected
properties of its base. Further, exactly how do you
add a new protected
property at runtime? Under both proposal-class-fields
and proposal-object-members, there is never any direct access to the
private container record, so use of Object.defineProperty
will never
work. IMO, any attempt to implement dynamic private properties in any
sensible and consistent fashion would require somehow exposing the private
data record to the code. That's a recipe for a private data leak. Not worth
it.
> 1. It's *super incredibly boilerplatey* and verbose syntactically. I'm not sure what you mean by "boilerplatey". As for being verbose, I'm just using the keywords everyone understands for this purpose. IMO, there's no advantage in trying to find some shorthand to do the same thing just because it saves a keystroke or two when it makes the code significantly more difficult to understand. > 2. `protected` on an object literal is next to useless. I've used that kind of feature almost never. I get where you're coming from with that. I don't see it being used very often (kinda like `with`), but it has to be there. If someone wants to use the facilities of `class` without the limitations of the keyword, and the intent is to build vertical hierarchies, they'll need the "protected" keyword on their prototype definition to share private data with descendant factories. It's even more necessary for people writing factory factories. The only other way to achieve the same thing would be to force them to use `Function()` or `eval` and build up the code as strings. I'd rather avoid that. > I also find it odd you're supporting private dynamic properties. How'd you get to the idea that I'm supporting dynamic private properties? The first 2 paragraphs in the implementation say that all private container records are sealed, and all fields in info records are added read-only. If it wasn't clear from that, I'm going to have to re-write that section. However, the intent is that after the declaration process is complete, what you have is all you can get. No additional private fields can be added later. I considered dynamic private data, but that can get very messy very quickly. > I actually think it's odd there is no attempt to implement dynamic properties in the other "private properties" proposals. It's not that odd. There are issues around inheritance when a subclass can remove the `protected` properties of its base. Further, exactly how do you add a new `protected` property at runtime? Under both proposal-class-fields and proposal-object-members, there is never any direct access to the private container record, so use of `Object.defineProperty` will never work. IMO, any attempt to implement dynamic private properties in any sensible and consistent fashion would require somehow exposing the private data record to the code. That's a recipe for a private data leak. Not worth it. -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180803/0c396f6e/attachment.html>
I'd argue that is 1:1 with the way subclasses work today.
Protected properties can be implemented by sharing the weakmap instance with the parent.
Private properties can be implemented by using a unique weakmap instance for the subclass.
I actually think it's pretty straightforward and simple.
I'd argue that is 1:1 with the way subclasses work today. Protected properties can be implemented by sharing the weakmap instance with the parent. Private properties can be implemented by using a unique weakmap instance for the subclass. I actually think it's pretty straightforward and simple. On Friday, August 3, 2018, Ranando King <kingmph at gmail.com> wrote: > > 1. It's *super incredibly boilerplatey* and verbose syntactically. > > I'm not sure what you mean by "boilerplatey". As for being verbose, I'm > just using the keywords everyone understands for this purpose. IMO, there's > no advantage in trying to find some shorthand to do the same thing just > because it saves a keystroke or two when it makes the code significantly > more difficult to understand. > > > 2. `protected` on an object literal is next to useless. I've used that kind > of feature almost never. > > I get where you're coming from with that. I don't see it being used very > often (kinda like `with`), but it has to be there. If someone wants to use > the facilities of `class` without the limitations of the keyword, and the > intent is to build vertical hierarchies, they'll need the "protected" > keyword on their prototype definition to share private data with descendant > factories. It's even more necessary for people writing factory factories. > The only other way to achieve the same thing would be to force them to use > `Function()` or `eval` and build up the code as strings. I'd rather avoid > that. > > > I also find it odd you're supporting private dynamic properties. > > How'd you get to the idea that I'm supporting dynamic private properties? > The first 2 paragraphs in the implementation say that all private container > records are sealed, and all fields in info records are added read-only. If > it wasn't clear from that, I'm going to have to re-write that section. > However, the intent is that after the declaration process is complete, what > you have is all you can get. No additional private fields can be added > later. I considered dynamic private data, but that can get very messy very > quickly. > > > I actually think it's odd there is no attempt to implement dynamic > properties in the other "private properties" proposals. > > It's not that odd. There are issues around inheritance when a subclass can > remove the `protected` properties of its base. Further, exactly how do you > add a new `protected` property at runtime? Under both proposal-class-fields > and proposal-object-members, there is never any direct access to the > private container record, so use of `Object.defineProperty` will never > work. IMO, any attempt to implement dynamic private properties in any > sensible and consistent fashion would require somehow exposing the private > data record to the code. That's a recipe for a private data leak. Not worth > it. > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180803/8594bee8/attachment-0001.html>
I'm glad someone other than myself can appreciate it. I trudged my way
through a lot of variations on the theme to find the implementation that
would make the most sense and still behave properly. It really is rather
simple. The POC will more or less show how it can be implemented. The only
issue I'm having at the moment is trying to capture access to super
properties. Apparently V8 doesn't redirect super
through prototypes like
I was expecting, so I'm beginning to think that I won't be able to
implement calls to protected methods of the base in this POC.
I'm glad someone other than myself can appreciate it. I trudged my way through a lot of variations on the theme to find the implementation that would make the most sense and still behave properly. It really is rather simple. The POC will more or less show how it can be implemented. The only issue I'm having at the moment is trying to capture access to `super` properties. Apparently V8 doesn't redirect `super` through prototypes like I was expecting, so I'm beginning to think that I won't be able to implement calls to protected methods of the base in this POC. On Fri, Aug 3, 2018 at 11:38 AM Michael Theriot < michael.lee.theriot at gmail.com> wrote: > I'd argue that is 1:1 with the way subclasses work today. > > Protected properties can be implemented by sharing the weakmap instance > with the parent. > > Private properties can be implemented by using a unique weakmap instance > for the subclass. > > I actually think it's pretty straightforward and simple. > > On Friday, August 3, 2018, Ranando King <kingmph at gmail.com> wrote: > >> > 1. It's *super incredibly boilerplatey* and verbose syntactically. >> >> I'm not sure what you mean by "boilerplatey". As for being verbose, I'm >> just using the keywords everyone understands for this purpose. IMO, there's >> no advantage in trying to find some shorthand to do the same thing just >> because it saves a keystroke or two when it makes the code significantly >> more difficult to understand. >> >> > 2. `protected` on an object literal is next to useless. I've used that kind >> of feature almost never. >> >> I get where you're coming from with that. I don't see it being used very >> often (kinda like `with`), but it has to be there. If someone wants to use >> the facilities of `class` without the limitations of the keyword, and the >> intent is to build vertical hierarchies, they'll need the "protected" >> keyword on their prototype definition to share private data with descendant >> factories. It's even more necessary for people writing factory factories. >> The only other way to achieve the same thing would be to force them to use >> `Function()` or `eval` and build up the code as strings. I'd rather avoid >> that. >> >> > I also find it odd you're supporting private dynamic properties. >> >> How'd you get to the idea that I'm supporting dynamic private properties? >> The first 2 paragraphs in the implementation say that all private container >> records are sealed, and all fields in info records are added read-only. If >> it wasn't clear from that, I'm going to have to re-write that section. >> However, the intent is that after the declaration process is complete, what >> you have is all you can get. No additional private fields can be added >> later. I considered dynamic private data, but that can get very messy very >> quickly. >> >> > I actually think it's odd there is no attempt to implement dynamic >> properties in the other "private properties" proposals. >> >> It's not that odd. There are issues around inheritance when a subclass >> can remove the `protected` properties of its base. Further, exactly how do >> you add a new `protected` property at runtime? Under both >> proposal-class-fields and proposal-object-members, there is never any >> direct access to the private container record, so use of >> `Object.defineProperty` will never work. IMO, any attempt to implement >> dynamic private properties in any sensible and consistent fashion would >> require somehow exposing the private data record to the code. That's a >> recipe for a private data leak. Not worth it. >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180803/7e0d12a0/attachment.html>
It certainly doesn't look or feel like JS - it feels more like Java or C#.
private
, protected
, class
, and a few other such keywords have all
been part of ES since long be for the TC39 board got their hands on it.
They hadn't been implemented, but they were all a very real part of ES. Now
that class
has been implemented, it makes little sense to leave behind
the private
and protected
keywords when we are trying to implement
their functionality.
private
looks like an identifier, and IMHO getters, setters, and async
functions suffer the same issue of the keyword seeming to blend in a little with surrounding code.
Have you ever thought that var
or let
look like identifiers? The
private
and protected
keywords serve the same role as var
and let
:
declaring a variable within a given scope or context. If you think there is
a good logical or rational reason to avoid using the keywords that have
been embedded in the language and left languishing, waiting for their
meaning to be implemented, then I'm willing to entertain that. If the
reason is based on mere feeling or emotion, well. I will only entertain
such arguments if my reason for doing things a certain way is equally
emotion based. Nothing I'm aware of in this proposal falls into that
category. I have logical reasons for every choice I've made.
protected
on an object literal is next to useless. I've used that kind
of feature almost never.
And how would that be accessible?
As you said, the vast majority of the time, this feature will go unused. However, when it's needed, it would look something like this:
var a = {
protected sharedData: 1,
increment() { ++this#.sharedData; },
print() { console.log(`sharedData = ${this#.sharedData}`); }
};
var b = {
__proto__: a,
decrement() { --this#.sharedData; }
};
Setting b.__proto__ = a
causes b.[[PrivateValues]].__proto__ = a.[[PrivateValues]]
, b.[[DeclarationInfo]].__proto__ = a.[[InheritanceInfo]]
, and b.[[InheritanceInfo]].proto = a.[[InheritanceInfo]]
. So it all just works.
I saw
obj#['key']
, which strongly suggests dynamic keys are supported.
Dynamic keys are supported. Dynamic properties are not! Please don't conflate the two. Dynamic keys are calculated property names. I am definitely supporting that. Dynamic properties refers to the ability to add and remove properties from an object at any time. I am not supporting that for private/protected members (unless someone can logically convince me it's a good idea).
> It certainly doesn't look or feel like JS - it feels more like Java or C#. `private`, `protected`, `class`, and a few other such keywords have all been part of ES since long be for the TC39 board got their hands on it. They hadn't been implemented, but they were all a very real part of ES. Now that `class` has been implemented, it makes little sense to leave behind the `private` and `protected` keywords when we are trying to implement their functionality. > `private` looks like an identifier, and IMHO getters, setters, and async functions suffer the same issue of the keyword seeming to blend in a little with surrounding code. Have you ever thought that `var` or `let` look like identifiers? The `private` and `protected` keywords serve the same role as `var` and `let`: declaring a variable within a given scope or context. If you think there is a good logical or rational reason to avoid using the keywords that have been embedded in the language and left languishing, waiting for their meaning to be implemented, then I'm willing to entertain that. If the reason is based on mere feeling or emotion, well. I will only entertain such arguments if my reason for doing things a certain way is equally emotion based. Nothing I'm aware of in this proposal falls into that category. I have logical reasons for every choice I've made. >> 2. `protected` on an object literal is next to useless. I've used that kind of feature almost never. > And how would that be accessible? As you said, the vast majority of the time, this feature will go unused. However, when it's needed, it would look something like this: ```js var a = { protected sharedData: 1, increment() { ++this#.sharedData; }, print() { console.log(`sharedData = ${this#.sharedData}`); } }; var b = { __proto__: a, decrement() { --this#.sharedData; } }; ``` Setting `b.__proto__ = a` causes `b.[[PrivateValues]].__proto__ = a.[[PrivateValues]]`, `b.[[DeclarationInfo]].__proto__ = a.[[InheritanceInfo]]`, and `b.[[InheritanceInfo]].proto = a.[[InheritanceInfo]]`. So it all just works. > I saw `obj#['key']`, which *strongly* suggests dynamic keys are supported. Dynamic **_keys_** are supported. Dynamic **_properties_** are not! Please don't conflate the two. Dynamic keys are calculated property names. I am definitely supporting that. Dynamic properties refers to the ability to add and remove properties from an object at any time. I am not supporting that for private/protected members (unless someone can logically convince me it's a good idea). On Fri, Aug 3, 2018 at 1:02 PM Isiah Meadows <isiahmeadows at gmail.com> wrote: > Inline > > On Fri, Aug 3, 2018, 11:12 Ranando King <kingmph at gmail.com> wrote: > >> > 1. It's *super incredibly boilerplatey* and verbose syntactically. >> >> I'm not sure what you mean by "boilerplatey". As for being verbose, I'm >> just using the keywords everyone understands for this purpose. IMO, there's >> no advantage in trying to find some shorthand to do the same thing just >> because it saves a keystroke or two when it makes the code significantly >> more difficult to understand. >> > > But on the same token, it's verbose enough that I feel readability starts > to suffer substantiallly. `private` looks like an identifier, and IMHO > getters, setters, and async functions suffer the same issue of the keyword > seeming to blend in a little with surrounding code. But those are more like > decorating the function than the name. > > Based on reading the several meeting notes, I don't believe the keyword > has been especially popular there, either. It certainly doesn't look or > feel like JS - it feels more like Java or C#. > > >> > 2. `protected` on an object literal is next to useless. I've used that kind >> of feature almost never. >> >> I get where you're coming from with that. I don't see it being used very >> often (kinda like `with`), but it has to be there. If someone wants to use >> the facilities of `class` without the limitations of the keyword, and the >> intent is to build vertical hierarchies, they'll need the "protected" >> keyword on their prototype definition to share private data with descendant >> factories. >> > > And how would that be accessible? Because you can't expose it via the same > way you do in classes without basically making them public (and several > workarounds suffer similar issues). > > > I also find it odd you're supporting private dynamic properties. >> >> How'd you get to the idea that I'm supporting dynamic private properties? >> > > I saw `obj#['key']`, which *strongly* suggests dynamic keys are supported. > > > I actually think it's odd there is no attempt to implement dynamic >> properties in the other "private properties" proposals. >> > >> It's not that odd. There are issues around inheritance when a subclass >> can remove the `protected` properties of its base. Further, exactly how do >> you add a new `protected` property at runtime? Under both >> proposal-class-fields and proposal-object-members, there is never any >> direct access to the private container record, so use of >> `Object.defineProperty` will never work. IMO, any attempt to implement >> dynamic private properties in any sensible and consistent fashion would >> require somehow exposing the private data record to the code. That's a >> recipe for a private data leak. Not worth it. >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180803/4eb6469e/attachment-0001.html>
My "private symbols" proposal supports it, but that's about it.
I think the main thing is that the use case isn't really that large, so nobody's really thought about it. (You can always "pretend" it exists by creating a single private key that's just an object dictionary.)
Isiah Meadows contact at isiahmeadows.com, www.isiahmeadows.com
My "private symbols" proposal supports it, but that's about it. I think the main thing is that the use case isn't really that large, so nobody's really thought about it. (You can always "pretend" it exists by creating a single private key that's just an object dictionary.) ----- Isiah Meadows contact at isiahmeadows.com www.isiahmeadows.com On Fri, Aug 3, 2018 at 8:34 AM, Michael Theriot <michael.lee.theriot at gmail.com> wrote: > If I understand the terminology, "private dynamic properties" are easily > polyfilled via weakmaps? > > I actually think it's odd there is no attempt to implement dynamic > properties in the other "private properties" proposals. > > > On Friday, August 3, 2018, Isiah Meadows <isiahmeadows at gmail.com> wrote: >> >> Okay, now that I look at that proposal, I see two issues right off: >> >> 1. It's *super incredibly boilerplatey* and verbose syntactically. I >> don't know very many people who'd be willing to downgrade very far >> from even what TypeScript has. (I'm specifically referring to the >> declarations here.) >> 2. `protected` on an object literal is next to useless. I've used that >> kind of feature almost never. >> >> I also find it odd you're supporting private dynamic properties. It >> does make polyfilling next to impossible, though. >> >> Just my 2 cents on it. (I glanced over this while very tired, so I >> probably missed several highlights. These are what stuck out to me.) >> >> ----- >> >> Isiah Meadows >> contact at isiahmeadows.com >> www.isiahmeadows.com >> >> >> On Wed, Aug 1, 2018 at 11:54 PM, Ranando King <kingmph at gmail.com> wrote: >> > https://github.com/rdking/proposal-object-members/blob/master/README.md >> > >> > On Wed, Aug 1, 2018 at 2:01 AM Isiah Meadows <isiahmeadows at gmail.com> >> > wrote: >> >> >> >> Do you have a link to this proposal so I can take a look at it? It'd >> >> be much easier to critique it if I could see the proposal text. >> >> ----- >> >> >> >> Isiah Meadows >> >> contact at isiahmeadows.com >> >> www.isiahmeadows.com >> >> >> >> >> >> On Wed, Aug 1, 2018 at 2:18 AM, Ranando King <kingmph at gmail.com> wrote: >> >> >> If you go back a few months, what you're proposing is *very* >> >> >> similar, >> >> >> at >> >> >> least functionally, to my previous iteration of my proposal: >> >> > >> >> > That functional similarity is intentional. After pouring over years >> >> > worth of >> >> > posts, I figured out what the vast majority of the >> >> > proposal-class-fields >> >> > detractors actually wanted: an elegant, easily recognized syntax for >> >> > adding >> >> > private members to objects. >> >> > >> >> >> My main problem was that trying to limit private properties to >> >> >> objects >> >> >> created within a scope got complicated in a hurry once you >> >> >> considered >> >> >> all >> >> >> the small details, and it just didn't seem simple anymore. >> >> > >> >> > I noticed that about your proposal too. I'm also pretty sure that >> >> > Daniel >> >> > E. >> >> > and Kevin G. ran into the same issues back during the >> >> > proposal-private-names >> >> > days which is why the private names concept is just an implementation >> >> > detail >> >> > in their current proposal. My proposal is made less complicated by >> >> > breaking >> >> > the problem down into the 3 pieces required to make it all work: >> >> > 1. a record to store private data >> >> > 2. an array to hold references to the schema records of accessible >> >> > private >> >> > data >> >> > 3. a schema record for the sharable data. >> >> > >> >> > In this way private = encapsulated on a non-function, protected = >> >> > private + >> >> > shared, and static = encapsulated on a function. It should be easy to >> >> > sort >> >> > out how the data would be stored given such simple definitions. These >> >> > simple >> >> > definitions also mean that encapsulation is naturally confined to >> >> > definitions. Attempts to alter that state lead to strange logical >> >> > contradictions and potential leaks of encapsulated data. I have >> >> > thought >> >> > of >> >> > the possibility that private data could be added after definition, >> >> > but >> >> > every >> >> > attempt I make to consider such a thing has so far led to a risk of >> >> > leaking. >> >> > >> >> > I've been working on some code that can serve as a proof-of-concept >> >> > in >> >> > ES6. >> >> > It will implement all of my proposal that can reasonably be >> >> > implemented >> >> > in >> >> > ES6 using Proxy. It's already in the proposal repository under the >> >> > POC >> >> > branch, but it's still a WIP. For now, it already supports inheriting >> >> > from >> >> > native objects. I'm working on subclassing right now. By the time I >> >> > get >> >> > done >> >> > (likely this coming Monday), it should support every feature in my >> >> > proposal. >> >> > I'm basically using it as a means to check the viability of my >> >> > proposal. >> >> > >> >> > On Tue, Jul 31, 2018 at 4:35 PM Isiah Meadows >> >> > <isiahmeadows at gmail.com> >> >> > wrote: >> >> >> >> >> >> If you go back a few months, what you're proposing is *very* >> >> >> similar, >> >> >> at least functionally, to my previous iteration of my proposal: >> >> >> >> >> >> >> >> >> >> >> >> https://github.com/isiahmeadows/private-symbol-proposal/blob/c5c9781d9e76123c92d8fbc83681fdd3a9b0b319/README.md >> >> >> >> >> >> My main problem was that trying to limit private properties to >> >> >> objects >> >> >> created within a scope got complicated in a hurry once you >> >> >> considered >> >> >> all the small details, and it just didn't seem simple anymore. It >> >> >> only >> >> >> got more complicated when you started getting into the logistics of >> >> >> integrating with modules. >> >> >> >> >> >> So I've considered the issue and explored it pretty thoroughly - I >> >> >> *really* don't want private data to be limited to classes (which I >> >> >> dislike), but I did also previously have the concern of trying to >> >> >> limit who could define properties where. >> >> >> >> >> >> I will point out that you can prevent arbitrary private extension by >> >> >> simply doing `Object.preventExtensions(object)`. Because properties >> >> >> defined using private symbols are otherwise just normal properties, >> >> >> they still have to go through the same access checks normal >> >> >> properties >> >> >> have to, like [[IsExtensible]]. The only other concrete difference >> >> >> is >> >> >> that proxy hooks don't fire when you do things with private symbols. >> >> >> >> >> >> ----- >> >> >> >> >> >> Isiah Meadows >> >> >> contact at isiahmeadows.com >> >> >> www.isiahmeadows.com >> >> >> >> >> >> >> >> >> On Tue, Jul 31, 2018 at 3:09 PM, Ranando King <kingmph at gmail.com> >> >> >> wrote: >> >> >> >> What use case are you referring to here? >> >> >> > >> >> >> > In the case of SymbolTree, the objects in use are external. >> >> >> > >> >> >> >> I think there’s been a misunderstanding. Everybody agrees that >> >> >> >> that’s a >> >> >> >> bad pattern. It’s not what the point of private symbols would be. >> >> >> >> It’s >> >> >> >> not a >> >> >> >> target use case. >> >> >> > >> >> >> > That certainly puts my mind at ease. >> >> >> > >> >> >> >> As Isiah said, “all of the examples here I've presented are for >> >> >> >> scenarios >> >> >> >> where the state is related to the factory that created the >> >> >> >> objects.” >> >> >> > >> >> >> > If the factory that creates the objects is the also the only thing >> >> >> > trying to >> >> >> > store private information on those objects, then I understand >> >> >> > you're >> >> >> > only >> >> >> > looking for per-instance module-private data, possibly with the >> >> >> > ability >> >> >> > to >> >> >> > use common private names. If that's the case, then it really is >> >> >> > just >> >> >> > 2 >> >> >> > simple extensions of my proposal: >> >> >> > * allow a Symbol when used as a private or protected property name >> >> >> > to >> >> >> > persist as the private Symbol name for the private instance field >> >> >> > on >> >> >> > each >> >> >> > object for which it is used. >> >> >> > * create an additional privilege level (internal) that places the >> >> >> > new >> >> >> > field's name in the [[DeclarationInfo]] of the function containing >> >> >> > the >> >> >> > declaration. >> >> >> > >> >> >> > The effect of using these 2 features together is that anything >> >> >> > within >> >> >> > the >> >> >> > same function as the declared Symbol will gain access to the >> >> >> > internal >> >> >> > field >> >> >> > of all objects using that Symbol as a field name. >> >> >> > >> >> >> > On Tue, Jul 31, 2018 at 1:36 PM Darien Valentine >> >> >> > <valentinium at gmail.com> >> >> >> > wrote: >> >> >> >> >> >> >> >> > I'd say you've identified the common pattern, but that pattern >> >> >> >> > itself >> >> >> >> > is >> >> >> >> > a bad use case, and the use of private symbols as you have >> >> >> >> > defined >> >> >> >> > them >> >> >> >> > doesn't do anything to correct the technical issue. >> >> >> >> >> >> >> >> I think there’s been a misunderstanding. Everybody agrees that >> >> >> >> that’s a >> >> >> >> bad pattern. It’s not what the point of private symbols would be. >> >> >> >> It’s >> >> >> >> not a >> >> >> >> target use case. >> >> >> >> >> >> >> >> > Since you cannot stick new properties onto a non-extensible >> >> >> >> > object, >> >> >> >> > even >> >> >> >> > private symbols won't solve the problem with your use case. >> >> >> >> >> >> >> >> That appending private symbols to external objects which are >> >> >> >> frozen >> >> >> >> wouldn’t work doesn’t matter precisely because it’s not a target >> >> >> >> use >> >> >> >> case. >> >> >> >> That it doesn’t work reliably might even be considered a >> >> >> >> positive, >> >> >> >> since it >> >> >> >> discourages something we all seem to agree is not good practice. >> >> >> >> >> >> >> >> It’s also not related to private symbols; this is already how >> >> >> >> properties >> >> >> >> work, regardless of what kind of key they have. >> >> >> >> >> >> >> >> > The difference here is that in your use cases, library A is >> >> >> >> > "sneakily" >> >> >> >> > storing information on object B. >> >> >> >> >> >> >> >> What use case are you referring to here? I can’t find any example >> >> >> >> in >> >> >> >> the >> >> >> >> previous posts that matches these descriptions. As Isiah said, >> >> >> >> “all >> >> >> >> of >> >> >> >> the >> >> >> >> examples here I've presented are for scenarios where the state is >> >> >> >> related to >> >> >> >> the factory that created the objects.” The same is true of my >> >> >> >> examples. >> >> >> >> Everybody’s on the same page regarding not wanting to add >> >> >> >> properties >> >> >> >> to >> >> >> >> objects their own libraries do not create. >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss
A keyword being reserved is NOT the same as "being a part of ES". package
is reserved too, but there's zero concept of packages in the language, and
absolutely no obligation for there ever to be one.
To reiterate, the existence of private
, public
, and protected
as
reserved keywords in no way justifies including any, or all, of these
keywords in any language feature.
A keyword being reserved is NOT the same as "being a part of ES". `package` is reserved too, but there's zero concept of packages in the language, and absolutely no obligation for there ever to be one. To reiterate, the existence of `private`, `public`, and `protected` as reserved keywords in *no way* justifies including any, or all, of these keywords in any language feature. On Fri, Aug 3, 2018 at 12:36 PM, Isiah Meadows <isiahmeadows at gmail.com> wrote: > My "private symbols" proposal supports it, but that's about it. > > I think the main thing is that the use case isn't really that large, > so nobody's really thought about it. (You can always "pretend" it > exists by creating a single private key that's just an object > dictionary.) > ----- > > Isiah Meadows > contact at isiahmeadows.com > www.isiahmeadows.com > > > On Fri, Aug 3, 2018 at 8:34 AM, Michael Theriot > <michael.lee.theriot at gmail.com> wrote: > > If I understand the terminology, "private dynamic properties" are easily > > polyfilled via weakmaps? > > > > I actually think it's odd there is no attempt to implement dynamic > > properties in the other "private properties" proposals. > > > > > > On Friday, August 3, 2018, Isiah Meadows <isiahmeadows at gmail.com> wrote: > >> > >> Okay, now that I look at that proposal, I see two issues right off: > >> > >> 1. It's *super incredibly boilerplatey* and verbose syntactically. I > >> don't know very many people who'd be willing to downgrade very far > >> from even what TypeScript has. (I'm specifically referring to the > >> declarations here.) > >> 2. `protected` on an object literal is next to useless. I've used that > >> kind of feature almost never. > >> > >> I also find it odd you're supporting private dynamic properties. It > >> does make polyfilling next to impossible, though. > >> > >> Just my 2 cents on it. (I glanced over this while very tired, so I > >> probably missed several highlights. These are what stuck out to me.) > >> > >> ----- > >> > >> Isiah Meadows > >> contact at isiahmeadows.com > >> www.isiahmeadows.com > >> > >> > >> On Wed, Aug 1, 2018 at 11:54 PM, Ranando King <kingmph at gmail.com> > wrote: > >> > https://github.com/rdking/proposal-object-members/blob/ > master/README.md > >> > > >> > On Wed, Aug 1, 2018 at 2:01 AM Isiah Meadows <isiahmeadows at gmail.com> > >> > wrote: > >> >> > >> >> Do you have a link to this proposal so I can take a look at it? It'd > >> >> be much easier to critique it if I could see the proposal text. > >> >> ----- > >> >> > >> >> Isiah Meadows > >> >> contact at isiahmeadows.com > >> >> www.isiahmeadows.com > >> >> > >> >> > >> >> On Wed, Aug 1, 2018 at 2:18 AM, Ranando King <kingmph at gmail.com> > wrote: > >> >> >> If you go back a few months, what you're proposing is *very* > >> >> >> similar, > >> >> >> at > >> >> >> least functionally, to my previous iteration of my proposal: > >> >> > > >> >> > That functional similarity is intentional. After pouring over years > >> >> > worth of > >> >> > posts, I figured out what the vast majority of the > >> >> > proposal-class-fields > >> >> > detractors actually wanted: an elegant, easily recognized syntax > for > >> >> > adding > >> >> > private members to objects. > >> >> > > >> >> >> My main problem was that trying to limit private properties to > >> >> >> objects > >> >> >> created within a scope got complicated in a hurry once you > >> >> >> considered > >> >> >> all > >> >> >> the small details, and it just didn't seem simple anymore. > >> >> > > >> >> > I noticed that about your proposal too. I'm also pretty sure that > >> >> > Daniel > >> >> > E. > >> >> > and Kevin G. ran into the same issues back during the > >> >> > proposal-private-names > >> >> > days which is why the private names concept is just an > implementation > >> >> > detail > >> >> > in their current proposal. My proposal is made less complicated by > >> >> > breaking > >> >> > the problem down into the 3 pieces required to make it all work: > >> >> > 1. a record to store private data > >> >> > 2. an array to hold references to the schema records of accessible > >> >> > private > >> >> > data > >> >> > 3. a schema record for the sharable data. > >> >> > > >> >> > In this way private = encapsulated on a non-function, protected = > >> >> > private + > >> >> > shared, and static = encapsulated on a function. It should be easy > to > >> >> > sort > >> >> > out how the data would be stored given such simple definitions. > These > >> >> > simple > >> >> > definitions also mean that encapsulation is naturally confined to > >> >> > definitions. Attempts to alter that state lead to strange logical > >> >> > contradictions and potential leaks of encapsulated data. I have > >> >> > thought > >> >> > of > >> >> > the possibility that private data could be added after definition, > >> >> > but > >> >> > every > >> >> > attempt I make to consider such a thing has so far led to a risk of > >> >> > leaking. > >> >> > > >> >> > I've been working on some code that can serve as a proof-of-concept > >> >> > in > >> >> > ES6. > >> >> > It will implement all of my proposal that can reasonably be > >> >> > implemented > >> >> > in > >> >> > ES6 using Proxy. It's already in the proposal repository under the > >> >> > POC > >> >> > branch, but it's still a WIP. For now, it already supports > inheriting > >> >> > from > >> >> > native objects. I'm working on subclassing right now. By the time I > >> >> > get > >> >> > done > >> >> > (likely this coming Monday), it should support every feature in my > >> >> > proposal. > >> >> > I'm basically using it as a means to check the viability of my > >> >> > proposal. > >> >> > > >> >> > On Tue, Jul 31, 2018 at 4:35 PM Isiah Meadows > >> >> > <isiahmeadows at gmail.com> > >> >> > wrote: > >> >> >> > >> >> >> If you go back a few months, what you're proposing is *very* > >> >> >> similar, > >> >> >> at least functionally, to my previous iteration of my proposal: > >> >> >> > >> >> >> > >> >> >> > >> >> >> https://github.com/isiahmeadows/private-symbol-proposal/blob/ > c5c9781d9e76123c92d8fbc83681fdd3a9b0b319/README.md > >> >> >> > >> >> >> My main problem was that trying to limit private properties to > >> >> >> objects > >> >> >> created within a scope got complicated in a hurry once you > >> >> >> considered > >> >> >> all the small details, and it just didn't seem simple anymore. It > >> >> >> only > >> >> >> got more complicated when you started getting into the logistics > of > >> >> >> integrating with modules. > >> >> >> > >> >> >> So I've considered the issue and explored it pretty thoroughly - I > >> >> >> *really* don't want private data to be limited to classes (which I > >> >> >> dislike), but I did also previously have the concern of trying to > >> >> >> limit who could define properties where. > >> >> >> > >> >> >> I will point out that you can prevent arbitrary private extension > by > >> >> >> simply doing `Object.preventExtensions(object)`. Because > properties > >> >> >> defined using private symbols are otherwise just normal > properties, > >> >> >> they still have to go through the same access checks normal > >> >> >> properties > >> >> >> have to, like [[IsExtensible]]. The only other concrete difference > >> >> >> is > >> >> >> that proxy hooks don't fire when you do things with private > symbols. > >> >> >> > >> >> >> ----- > >> >> >> > >> >> >> Isiah Meadows > >> >> >> contact at isiahmeadows.com > >> >> >> www.isiahmeadows.com > >> >> >> > >> >> >> > >> >> >> On Tue, Jul 31, 2018 at 3:09 PM, Ranando King <kingmph at gmail.com> > >> >> >> wrote: > >> >> >> >> What use case are you referring to here? > >> >> >> > > >> >> >> > In the case of SymbolTree, the objects in use are external. > >> >> >> > > >> >> >> >> I think there’s been a misunderstanding. Everybody agrees that > >> >> >> >> that’s a > >> >> >> >> bad pattern. It’s not what the point of private symbols would > be. > >> >> >> >> It’s > >> >> >> >> not a > >> >> >> >> target use case. > >> >> >> > > >> >> >> > That certainly puts my mind at ease. > >> >> >> > > >> >> >> >> As Isiah said, “all of the examples here I've presented are for > >> >> >> >> scenarios > >> >> >> >> where the state is related to the factory that created the > >> >> >> >> objects.” > >> >> >> > > >> >> >> > If the factory that creates the objects is the also the only > thing > >> >> >> > trying to > >> >> >> > store private information on those objects, then I understand > >> >> >> > you're > >> >> >> > only > >> >> >> > looking for per-instance module-private data, possibly with the > >> >> >> > ability > >> >> >> > to > >> >> >> > use common private names. If that's the case, then it really is > >> >> >> > just > >> >> >> > 2 > >> >> >> > simple extensions of my proposal: > >> >> >> > * allow a Symbol when used as a private or protected property > name > >> >> >> > to > >> >> >> > persist as the private Symbol name for the private instance > field > >> >> >> > on > >> >> >> > each > >> >> >> > object for which it is used. > >> >> >> > * create an additional privilege level (internal) that places > the > >> >> >> > new > >> >> >> > field's name in the [[DeclarationInfo]] of the function > containing > >> >> >> > the > >> >> >> > declaration. > >> >> >> > > >> >> >> > The effect of using these 2 features together is that anything > >> >> >> > within > >> >> >> > the > >> >> >> > same function as the declared Symbol will gain access to the > >> >> >> > internal > >> >> >> > field > >> >> >> > of all objects using that Symbol as a field name. > >> >> >> > > >> >> >> > On Tue, Jul 31, 2018 at 1:36 PM Darien Valentine > >> >> >> > <valentinium at gmail.com> > >> >> >> > wrote: > >> >> >> >> > >> >> >> >> > I'd say you've identified the common pattern, but that > pattern > >> >> >> >> > itself > >> >> >> >> > is > >> >> >> >> > a bad use case, and the use of private symbols as you have > >> >> >> >> > defined > >> >> >> >> > them > >> >> >> >> > doesn't do anything to correct the technical issue. > >> >> >> >> > >> >> >> >> I think there’s been a misunderstanding. Everybody agrees that > >> >> >> >> that’s a > >> >> >> >> bad pattern. It’s not what the point of private symbols would > be. > >> >> >> >> It’s > >> >> >> >> not a > >> >> >> >> target use case. > >> >> >> >> > >> >> >> >> > Since you cannot stick new properties onto a non-extensible > >> >> >> >> > object, > >> >> >> >> > even > >> >> >> >> > private symbols won't solve the problem with your use case. > >> >> >> >> > >> >> >> >> That appending private symbols to external objects which are > >> >> >> >> frozen > >> >> >> >> wouldn’t work doesn’t matter precisely because it’s not a > target > >> >> >> >> use > >> >> >> >> case. > >> >> >> >> That it doesn’t work reliably might even be considered a > >> >> >> >> positive, > >> >> >> >> since it > >> >> >> >> discourages something we all seem to agree is not good > practice. > >> >> >> >> > >> >> >> >> It’s also not related to private symbols; this is already how > >> >> >> >> properties > >> >> >> >> work, regardless of what kind of key they have. > >> >> >> >> > >> >> >> >> > The difference here is that in your use cases, library A is > >> >> >> >> > "sneakily" > >> >> >> >> > storing information on object B. > >> >> >> >> > >> >> >> >> What use case are you referring to here? I can’t find any > example > >> >> >> >> in > >> >> >> >> the > >> >> >> >> previous posts that matches these descriptions. As Isiah said, > >> >> >> >> “all > >> >> >> >> of > >> >> >> >> the > >> >> >> >> examples here I've presented are for scenarios where the state > is > >> >> >> >> related to > >> >> >> >> the factory that created the objects.” The same is true of my > >> >> >> >> examples. > >> >> >> >> Everybody’s on the same page regarding not wanting to add > >> >> >> >> properties > >> >> >> >> to > >> >> >> >> objects their own libraries do not create. > >> _______________________________________________ > >> 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/20180803/f89eb467/attachment-0001.html>
Good argument. However, the fact that the wide adoption private
and
public
in other languages constitutes an immediately understood,
well-recognized syntax for declaring privilege levels in languages with
classes is my primary reason for adopting those already reserved keywords.
The fact that I also think it would be a waste not to use them is just a
personal bias I'm willing to disregard in the face of a good, sound,
logical reason for not using them.
Good argument. However, the fact that the wide adoption `private` and `public` in other languages constitutes an immediately understood, well-recognized syntax for declaring privilege levels in languages with classes is my primary reason for adopting those already reserved keywords. The fact that I also think it would be a waste not to use them is just a personal bias I'm willing to disregard in the face of a good, sound, logical reason for not using them. On Fri, Aug 3, 2018 at 4:07 PM Jordan Harband <ljharb at gmail.com> wrote: > A keyword being reserved is NOT the same as "being a part of ES". > `package` is reserved too, but there's zero concept of packages in the > language, and absolutely no obligation for there ever to be one. > > To reiterate, the existence of `private`, `public`, and `protected` as > reserved keywords in *no way* justifies including any, or all, of these > keywords in any language feature. > > On Fri, Aug 3, 2018 at 12:36 PM, Isiah Meadows <isiahmeadows at gmail.com> > wrote: > >> My "private symbols" proposal supports it, but that's about it. >> >> I think the main thing is that the use case isn't really that large, >> so nobody's really thought about it. (You can always "pretend" it >> exists by creating a single private key that's just an object >> dictionary.) >> ----- >> >> Isiah Meadows >> contact at isiahmeadows.com >> www.isiahmeadows.com >> >> >> On Fri, Aug 3, 2018 at 8:34 AM, Michael Theriot >> <michael.lee.theriot at gmail.com> wrote: >> > If I understand the terminology, "private dynamic properties" are easily >> > polyfilled via weakmaps? >> > >> > I actually think it's odd there is no attempt to implement dynamic >> > properties in the other "private properties" proposals. >> > >> > >> > On Friday, August 3, 2018, Isiah Meadows <isiahmeadows at gmail.com> >> wrote: >> >> >> >> Okay, now that I look at that proposal, I see two issues right off: >> >> >> >> 1. It's *super incredibly boilerplatey* and verbose syntactically. I >> >> don't know very many people who'd be willing to downgrade very far >> >> from even what TypeScript has. (I'm specifically referring to the >> >> declarations here.) >> >> 2. `protected` on an object literal is next to useless. I've used that >> >> kind of feature almost never. >> >> >> >> I also find it odd you're supporting private dynamic properties. It >> >> does make polyfilling next to impossible, though. >> >> >> >> Just my 2 cents on it. (I glanced over this while very tired, so I >> >> probably missed several highlights. These are what stuck out to me.) >> >> >> >> ----- >> >> >> >> Isiah Meadows >> >> contact at isiahmeadows.com >> >> www.isiahmeadows.com >> >> >> >> >> >> On Wed, Aug 1, 2018 at 11:54 PM, Ranando King <kingmph at gmail.com> >> wrote: >> >> > >> https://github.com/rdking/proposal-object-members/blob/master/README.md >> >> > >> >> > On Wed, Aug 1, 2018 at 2:01 AM Isiah Meadows <isiahmeadows at gmail.com >> > >> >> > wrote: >> >> >> >> >> >> Do you have a link to this proposal so I can take a look at it? It'd >> >> >> be much easier to critique it if I could see the proposal text. >> >> >> ----- >> >> >> >> >> >> Isiah Meadows >> >> >> contact at isiahmeadows.com >> >> >> www.isiahmeadows.com >> >> >> >> >> >> >> >> >> On Wed, Aug 1, 2018 at 2:18 AM, Ranando King <kingmph at gmail.com> >> wrote: >> >> >> >> If you go back a few months, what you're proposing is *very* >> >> >> >> similar, >> >> >> >> at >> >> >> >> least functionally, to my previous iteration of my proposal: >> >> >> > >> >> >> > That functional similarity is intentional. After pouring over >> years >> >> >> > worth of >> >> >> > posts, I figured out what the vast majority of the >> >> >> > proposal-class-fields >> >> >> > detractors actually wanted: an elegant, easily recognized syntax >> for >> >> >> > adding >> >> >> > private members to objects. >> >> >> > >> >> >> >> My main problem was that trying to limit private properties to >> >> >> >> objects >> >> >> >> created within a scope got complicated in a hurry once you >> >> >> >> considered >> >> >> >> all >> >> >> >> the small details, and it just didn't seem simple anymore. >> >> >> > >> >> >> > I noticed that about your proposal too. I'm also pretty sure that >> >> >> > Daniel >> >> >> > E. >> >> >> > and Kevin G. ran into the same issues back during the >> >> >> > proposal-private-names >> >> >> > days which is why the private names concept is just an >> implementation >> >> >> > detail >> >> >> > in their current proposal. My proposal is made less complicated by >> >> >> > breaking >> >> >> > the problem down into the 3 pieces required to make it all work: >> >> >> > 1. a record to store private data >> >> >> > 2. an array to hold references to the schema records of accessible >> >> >> > private >> >> >> > data >> >> >> > 3. a schema record for the sharable data. >> >> >> > >> >> >> > In this way private = encapsulated on a non-function, protected = >> >> >> > private + >> >> >> > shared, and static = encapsulated on a function. It should be >> easy to >> >> >> > sort >> >> >> > out how the data would be stored given such simple definitions. >> These >> >> >> > simple >> >> >> > definitions also mean that encapsulation is naturally confined to >> >> >> > definitions. Attempts to alter that state lead to strange logical >> >> >> > contradictions and potential leaks of encapsulated data. I have >> >> >> > thought >> >> >> > of >> >> >> > the possibility that private data could be added after definition, >> >> >> > but >> >> >> > every >> >> >> > attempt I make to consider such a thing has so far led to a risk >> of >> >> >> > leaking. >> >> >> > >> >> >> > I've been working on some code that can serve as a >> proof-of-concept >> >> >> > in >> >> >> > ES6. >> >> >> > It will implement all of my proposal that can reasonably be >> >> >> > implemented >> >> >> > in >> >> >> > ES6 using Proxy. It's already in the proposal repository under the >> >> >> > POC >> >> >> > branch, but it's still a WIP. For now, it already supports >> inheriting >> >> >> > from >> >> >> > native objects. I'm working on subclassing right now. By the time >> I >> >> >> > get >> >> >> > done >> >> >> > (likely this coming Monday), it should support every feature in my >> >> >> > proposal. >> >> >> > I'm basically using it as a means to check the viability of my >> >> >> > proposal. >> >> >> > >> >> >> > On Tue, Jul 31, 2018 at 4:35 PM Isiah Meadows >> >> >> > <isiahmeadows at gmail.com> >> >> >> > wrote: >> >> >> >> >> >> >> >> If you go back a few months, what you're proposing is *very* >> >> >> >> similar, >> >> >> >> at least functionally, to my previous iteration of my proposal: >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> https://github.com/isiahmeadows/private-symbol-proposal/blob/c5c9781d9e76123c92d8fbc83681fdd3a9b0b319/README.md >> >> >> >> >> >> >> >> My main problem was that trying to limit private properties to >> >> >> >> objects >> >> >> >> created within a scope got complicated in a hurry once you >> >> >> >> considered >> >> >> >> all the small details, and it just didn't seem simple anymore. It >> >> >> >> only >> >> >> >> got more complicated when you started getting into the logistics >> of >> >> >> >> integrating with modules. >> >> >> >> >> >> >> >> So I've considered the issue and explored it pretty thoroughly - >> I >> >> >> >> *really* don't want private data to be limited to classes (which >> I >> >> >> >> dislike), but I did also previously have the concern of trying to >> >> >> >> limit who could define properties where. >> >> >> >> >> >> >> >> I will point out that you can prevent arbitrary private >> extension by >> >> >> >> simply doing `Object.preventExtensions(object)`. Because >> properties >> >> >> >> defined using private symbols are otherwise just normal >> properties, >> >> >> >> they still have to go through the same access checks normal >> >> >> >> properties >> >> >> >> have to, like [[IsExtensible]]. The only other concrete >> difference >> >> >> >> is >> >> >> >> that proxy hooks don't fire when you do things with private >> symbols. >> >> >> >> >> >> >> >> ----- >> >> >> >> >> >> >> >> Isiah Meadows >> >> >> >> contact at isiahmeadows.com >> >> >> >> www.isiahmeadows.com >> >> >> >> >> >> >> >> >> >> >> >> On Tue, Jul 31, 2018 at 3:09 PM, Ranando King <kingmph at gmail.com >> > >> >> >> >> wrote: >> >> >> >> >> What use case are you referring to here? >> >> >> >> > >> >> >> >> > In the case of SymbolTree, the objects in use are external. >> >> >> >> > >> >> >> >> >> I think there’s been a misunderstanding. Everybody agrees that >> >> >> >> >> that’s a >> >> >> >> >> bad pattern. It’s not what the point of private symbols would >> be. >> >> >> >> >> It’s >> >> >> >> >> not a >> >> >> >> >> target use case. >> >> >> >> > >> >> >> >> > That certainly puts my mind at ease. >> >> >> >> > >> >> >> >> >> As Isiah said, “all of the examples here I've presented are >> for >> >> >> >> >> scenarios >> >> >> >> >> where the state is related to the factory that created the >> >> >> >> >> objects.” >> >> >> >> > >> >> >> >> > If the factory that creates the objects is the also the only >> thing >> >> >> >> > trying to >> >> >> >> > store private information on those objects, then I understand >> >> >> >> > you're >> >> >> >> > only >> >> >> >> > looking for per-instance module-private data, possibly with the >> >> >> >> > ability >> >> >> >> > to >> >> >> >> > use common private names. If that's the case, then it really is >> >> >> >> > just >> >> >> >> > 2 >> >> >> >> > simple extensions of my proposal: >> >> >> >> > * allow a Symbol when used as a private or protected property >> name >> >> >> >> > to >> >> >> >> > persist as the private Symbol name for the private instance >> field >> >> >> >> > on >> >> >> >> > each >> >> >> >> > object for which it is used. >> >> >> >> > * create an additional privilege level (internal) that places >> the >> >> >> >> > new >> >> >> >> > field's name in the [[DeclarationInfo]] of the function >> containing >> >> >> >> > the >> >> >> >> > declaration. >> >> >> >> > >> >> >> >> > The effect of using these 2 features together is that anything >> >> >> >> > within >> >> >> >> > the >> >> >> >> > same function as the declared Symbol will gain access to the >> >> >> >> > internal >> >> >> >> > field >> >> >> >> > of all objects using that Symbol as a field name. >> >> >> >> > >> >> >> >> > On Tue, Jul 31, 2018 at 1:36 PM Darien Valentine >> >> >> >> > <valentinium at gmail.com> >> >> >> >> > wrote: >> >> >> >> >> >> >> >> >> >> > I'd say you've identified the common pattern, but that >> pattern >> >> >> >> >> > itself >> >> >> >> >> > is >> >> >> >> >> > a bad use case, and the use of private symbols as you have >> >> >> >> >> > defined >> >> >> >> >> > them >> >> >> >> >> > doesn't do anything to correct the technical issue. >> >> >> >> >> >> >> >> >> >> I think there’s been a misunderstanding. Everybody agrees that >> >> >> >> >> that’s a >> >> >> >> >> bad pattern. It’s not what the point of private symbols would >> be. >> >> >> >> >> It’s >> >> >> >> >> not a >> >> >> >> >> target use case. >> >> >> >> >> >> >> >> >> >> > Since you cannot stick new properties onto a non-extensible >> >> >> >> >> > object, >> >> >> >> >> > even >> >> >> >> >> > private symbols won't solve the problem with your use case. >> >> >> >> >> >> >> >> >> >> That appending private symbols to external objects which are >> >> >> >> >> frozen >> >> >> >> >> wouldn’t work doesn’t matter precisely because it’s not a >> target >> >> >> >> >> use >> >> >> >> >> case. >> >> >> >> >> That it doesn’t work reliably might even be considered a >> >> >> >> >> positive, >> >> >> >> >> since it >> >> >> >> >> discourages something we all seem to agree is not good >> practice. >> >> >> >> >> >> >> >> >> >> It’s also not related to private symbols; this is already how >> >> >> >> >> properties >> >> >> >> >> work, regardless of what kind of key they have. >> >> >> >> >> >> >> >> >> >> > The difference here is that in your use cases, library A is >> >> >> >> >> > "sneakily" >> >> >> >> >> > storing information on object B. >> >> >> >> >> >> >> >> >> >> What use case are you referring to here? I can’t find any >> example >> >> >> >> >> in >> >> >> >> >> the >> >> >> >> >> previous posts that matches these descriptions. As Isiah said, >> >> >> >> >> “all >> >> >> >> >> of >> >> >> >> >> the >> >> >> >> >> examples here I've presented are for scenarios where the >> state is >> >> >> >> >> related to >> >> >> >> >> the factory that created the objects.” The same is true of my >> >> >> >> >> examples. >> >> >> >> >> Everybody’s on the same page regarding not wanting to add >> >> >> >> >> properties >> >> >> >> >> to >> >> >> >> >> objects their own libraries do not create. >> >> _______________________________________________ >> >> 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/20180803/96de148b/attachment-0001.html>
A side thought:
If a language reserving certain words, even to the point of generating
error messages related to using them under certain circumstances, doesn't
constitute at least part of a justification for using them, then why do
languages so often reserve keywords for future use? Isn't precisely the
case with private
, protected
, public
, and package
(and class
prior to ES6). Weren't they all holdovers from the fact that the syntax for
ES was mostly borrowed from Java, and kept in reserve just in case the
concepts behind these keywords became language features?
If that's not the case, then there's no point in keeping these (or indeed any) keywords in reserve.
A side thought: If a language reserving certain words, even to the point of generating error messages related to using them under certain circumstances, doesn't constitute at least part of a justification for using them, then why do languages so often reserve keywords for future use? Isn't precisely the case with `private`, `protected`, `public`, and `package` (and `class` prior to ES6). Weren't they all holdovers from the fact that the syntax for ES was mostly borrowed from Java, and kept in reserve just in case the concepts behind these keywords became language features? If that's not the case, then there's no point in keeping these (or indeed any) keywords in reserve. On Fri, Aug 3, 2018 at 4:24 PM Ranando King <kingmph at gmail.com> wrote: > Good argument. However, the fact that the wide adoption `private` and > `public` in other languages constitutes an immediately understood, > well-recognized syntax for declaring privilege levels in languages with > classes is my primary reason for adopting those already reserved keywords. > The fact that I also think it would be a waste not to use them is just a > personal bias I'm willing to disregard in the face of a good, sound, > logical reason for not using them. > > On Fri, Aug 3, 2018 at 4:07 PM Jordan Harband <ljharb at gmail.com> wrote: > >> A keyword being reserved is NOT the same as "being a part of ES". >> `package` is reserved too, but there's zero concept of packages in the >> language, and absolutely no obligation for there ever to be one. >> >> To reiterate, the existence of `private`, `public`, and `protected` as >> reserved keywords in *no way* justifies including any, or all, of these >> keywords in any language feature. >> >> On Fri, Aug 3, 2018 at 12:36 PM, Isiah Meadows <isiahmeadows at gmail.com> >> wrote: >> >>> My "private symbols" proposal supports it, but that's about it. >>> >>> I think the main thing is that the use case isn't really that large, >>> so nobody's really thought about it. (You can always "pretend" it >>> exists by creating a single private key that's just an object >>> dictionary.) >>> ----- >>> >>> Isiah Meadows >>> contact at isiahmeadows.com >>> www.isiahmeadows.com >>> >>> >>> On Fri, Aug 3, 2018 at 8:34 AM, Michael Theriot >>> <michael.lee.theriot at gmail.com> wrote: >>> > If I understand the terminology, "private dynamic properties" are >>> easily >>> > polyfilled via weakmaps? >>> > >>> > I actually think it's odd there is no attempt to implement dynamic >>> > properties in the other "private properties" proposals. >>> > >>> > >>> > On Friday, August 3, 2018, Isiah Meadows <isiahmeadows at gmail.com> >>> wrote: >>> >> >>> >> Okay, now that I look at that proposal, I see two issues right off: >>> >> >>> >> 1. It's *super incredibly boilerplatey* and verbose syntactically. I >>> >> don't know very many people who'd be willing to downgrade very far >>> >> from even what TypeScript has. (I'm specifically referring to the >>> >> declarations here.) >>> >> 2. `protected` on an object literal is next to useless. I've used that >>> >> kind of feature almost never. >>> >> >>> >> I also find it odd you're supporting private dynamic properties. It >>> >> does make polyfilling next to impossible, though. >>> >> >>> >> Just my 2 cents on it. (I glanced over this while very tired, so I >>> >> probably missed several highlights. These are what stuck out to me.) >>> >> >>> >> ----- >>> >> >>> >> Isiah Meadows >>> >> contact at isiahmeadows.com >>> >> www.isiahmeadows.com >>> >> >>> >> >>> >> On Wed, Aug 1, 2018 at 11:54 PM, Ranando King <kingmph at gmail.com> >>> wrote: >>> >> > >>> https://github.com/rdking/proposal-object-members/blob/master/README.md >>> >> > >>> >> > On Wed, Aug 1, 2018 at 2:01 AM Isiah Meadows < >>> isiahmeadows at gmail.com> >>> >> > wrote: >>> >> >> >>> >> >> Do you have a link to this proposal so I can take a look at it? >>> It'd >>> >> >> be much easier to critique it if I could see the proposal text. >>> >> >> ----- >>> >> >> >>> >> >> Isiah Meadows >>> >> >> contact at isiahmeadows.com >>> >> >> www.isiahmeadows.com >>> >> >> >>> >> >> >>> >> >> On Wed, Aug 1, 2018 at 2:18 AM, Ranando King <kingmph at gmail.com> >>> wrote: >>> >> >> >> If you go back a few months, what you're proposing is *very* >>> >> >> >> similar, >>> >> >> >> at >>> >> >> >> least functionally, to my previous iteration of my proposal: >>> >> >> > >>> >> >> > That functional similarity is intentional. After pouring over >>> years >>> >> >> > worth of >>> >> >> > posts, I figured out what the vast majority of the >>> >> >> > proposal-class-fields >>> >> >> > detractors actually wanted: an elegant, easily recognized syntax >>> for >>> >> >> > adding >>> >> >> > private members to objects. >>> >> >> > >>> >> >> >> My main problem was that trying to limit private properties to >>> >> >> >> objects >>> >> >> >> created within a scope got complicated in a hurry once you >>> >> >> >> considered >>> >> >> >> all >>> >> >> >> the small details, and it just didn't seem simple anymore. >>> >> >> > >>> >> >> > I noticed that about your proposal too. I'm also pretty sure that >>> >> >> > Daniel >>> >> >> > E. >>> >> >> > and Kevin G. ran into the same issues back during the >>> >> >> > proposal-private-names >>> >> >> > days which is why the private names concept is just an >>> implementation >>> >> >> > detail >>> >> >> > in their current proposal. My proposal is made less complicated >>> by >>> >> >> > breaking >>> >> >> > the problem down into the 3 pieces required to make it all work: >>> >> >> > 1. a record to store private data >>> >> >> > 2. an array to hold references to the schema records of >>> accessible >>> >> >> > private >>> >> >> > data >>> >> >> > 3. a schema record for the sharable data. >>> >> >> > >>> >> >> > In this way private = encapsulated on a non-function, protected = >>> >> >> > private + >>> >> >> > shared, and static = encapsulated on a function. It should be >>> easy to >>> >> >> > sort >>> >> >> > out how the data would be stored given such simple definitions. >>> These >>> >> >> > simple >>> >> >> > definitions also mean that encapsulation is naturally confined to >>> >> >> > definitions. Attempts to alter that state lead to strange logical >>> >> >> > contradictions and potential leaks of encapsulated data. I have >>> >> >> > thought >>> >> >> > of >>> >> >> > the possibility that private data could be added after >>> definition, >>> >> >> > but >>> >> >> > every >>> >> >> > attempt I make to consider such a thing has so far led to a risk >>> of >>> >> >> > leaking. >>> >> >> > >>> >> >> > I've been working on some code that can serve as a >>> proof-of-concept >>> >> >> > in >>> >> >> > ES6. >>> >> >> > It will implement all of my proposal that can reasonably be >>> >> >> > implemented >>> >> >> > in >>> >> >> > ES6 using Proxy. It's already in the proposal repository under >>> the >>> >> >> > POC >>> >> >> > branch, but it's still a WIP. For now, it already supports >>> inheriting >>> >> >> > from >>> >> >> > native objects. I'm working on subclassing right now. By the >>> time I >>> >> >> > get >>> >> >> > done >>> >> >> > (likely this coming Monday), it should support every feature in >>> my >>> >> >> > proposal. >>> >> >> > I'm basically using it as a means to check the viability of my >>> >> >> > proposal. >>> >> >> > >>> >> >> > On Tue, Jul 31, 2018 at 4:35 PM Isiah Meadows >>> >> >> > <isiahmeadows at gmail.com> >>> >> >> > wrote: >>> >> >> >> >>> >> >> >> If you go back a few months, what you're proposing is *very* >>> >> >> >> similar, >>> >> >> >> at least functionally, to my previous iteration of my proposal: >>> >> >> >> >>> >> >> >> >>> >> >> >> >>> >> >> >> >>> https://github.com/isiahmeadows/private-symbol-proposal/blob/c5c9781d9e76123c92d8fbc83681fdd3a9b0b319/README.md >>> >> >> >> >>> >> >> >> My main problem was that trying to limit private properties to >>> >> >> >> objects >>> >> >> >> created within a scope got complicated in a hurry once you >>> >> >> >> considered >>> >> >> >> all the small details, and it just didn't seem simple anymore. >>> It >>> >> >> >> only >>> >> >> >> got more complicated when you started getting into the >>> logistics of >>> >> >> >> integrating with modules. >>> >> >> >> >>> >> >> >> So I've considered the issue and explored it pretty thoroughly >>> - I >>> >> >> >> *really* don't want private data to be limited to classes >>> (which I >>> >> >> >> dislike), but I did also previously have the concern of trying >>> to >>> >> >> >> limit who could define properties where. >>> >> >> >> >>> >> >> >> I will point out that you can prevent arbitrary private >>> extension by >>> >> >> >> simply doing `Object.preventExtensions(object)`. Because >>> properties >>> >> >> >> defined using private symbols are otherwise just normal >>> properties, >>> >> >> >> they still have to go through the same access checks normal >>> >> >> >> properties >>> >> >> >> have to, like [[IsExtensible]]. The only other concrete >>> difference >>> >> >> >> is >>> >> >> >> that proxy hooks don't fire when you do things with private >>> symbols. >>> >> >> >> >>> >> >> >> ----- >>> >> >> >> >>> >> >> >> Isiah Meadows >>> >> >> >> contact at isiahmeadows.com >>> >> >> >> www.isiahmeadows.com >>> >> >> >> >>> >> >> >> >>> >> >> >> On Tue, Jul 31, 2018 at 3:09 PM, Ranando King < >>> kingmph at gmail.com> >>> >> >> >> wrote: >>> >> >> >> >> What use case are you referring to here? >>> >> >> >> > >>> >> >> >> > In the case of SymbolTree, the objects in use are external. >>> >> >> >> > >>> >> >> >> >> I think there’s been a misunderstanding. Everybody agrees >>> that >>> >> >> >> >> that’s a >>> >> >> >> >> bad pattern. It’s not what the point of private symbols >>> would be. >>> >> >> >> >> It’s >>> >> >> >> >> not a >>> >> >> >> >> target use case. >>> >> >> >> > >>> >> >> >> > That certainly puts my mind at ease. >>> >> >> >> > >>> >> >> >> >> As Isiah said, “all of the examples here I've presented are >>> for >>> >> >> >> >> scenarios >>> >> >> >> >> where the state is related to the factory that created the >>> >> >> >> >> objects.” >>> >> >> >> > >>> >> >> >> > If the factory that creates the objects is the also the only >>> thing >>> >> >> >> > trying to >>> >> >> >> > store private information on those objects, then I understand >>> >> >> >> > you're >>> >> >> >> > only >>> >> >> >> > looking for per-instance module-private data, possibly with >>> the >>> >> >> >> > ability >>> >> >> >> > to >>> >> >> >> > use common private names. If that's the case, then it really >>> is >>> >> >> >> > just >>> >> >> >> > 2 >>> >> >> >> > simple extensions of my proposal: >>> >> >> >> > * allow a Symbol when used as a private or protected property >>> name >>> >> >> >> > to >>> >> >> >> > persist as the private Symbol name for the private instance >>> field >>> >> >> >> > on >>> >> >> >> > each >>> >> >> >> > object for which it is used. >>> >> >> >> > * create an additional privilege level (internal) that places >>> the >>> >> >> >> > new >>> >> >> >> > field's name in the [[DeclarationInfo]] of the function >>> containing >>> >> >> >> > the >>> >> >> >> > declaration. >>> >> >> >> > >>> >> >> >> > The effect of using these 2 features together is that anything >>> >> >> >> > within >>> >> >> >> > the >>> >> >> >> > same function as the declared Symbol will gain access to the >>> >> >> >> > internal >>> >> >> >> > field >>> >> >> >> > of all objects using that Symbol as a field name. >>> >> >> >> > >>> >> >> >> > On Tue, Jul 31, 2018 at 1:36 PM Darien Valentine >>> >> >> >> > <valentinium at gmail.com> >>> >> >> >> > wrote: >>> >> >> >> >> >>> >> >> >> >> > I'd say you've identified the common pattern, but that >>> pattern >>> >> >> >> >> > itself >>> >> >> >> >> > is >>> >> >> >> >> > a bad use case, and the use of private symbols as you have >>> >> >> >> >> > defined >>> >> >> >> >> > them >>> >> >> >> >> > doesn't do anything to correct the technical issue. >>> >> >> >> >> >>> >> >> >> >> I think there’s been a misunderstanding. Everybody agrees >>> that >>> >> >> >> >> that’s a >>> >> >> >> >> bad pattern. It’s not what the point of private symbols >>> would be. >>> >> >> >> >> It’s >>> >> >> >> >> not a >>> >> >> >> >> target use case. >>> >> >> >> >> >>> >> >> >> >> > Since you cannot stick new properties onto a non-extensible >>> >> >> >> >> > object, >>> >> >> >> >> > even >>> >> >> >> >> > private symbols won't solve the problem with your use case. >>> >> >> >> >> >>> >> >> >> >> That appending private symbols to external objects which are >>> >> >> >> >> frozen >>> >> >> >> >> wouldn’t work doesn’t matter precisely because it’s not a >>> target >>> >> >> >> >> use >>> >> >> >> >> case. >>> >> >> >> >> That it doesn’t work reliably might even be considered a >>> >> >> >> >> positive, >>> >> >> >> >> since it >>> >> >> >> >> discourages something we all seem to agree is not good >>> practice. >>> >> >> >> >> >>> >> >> >> >> It’s also not related to private symbols; this is already how >>> >> >> >> >> properties >>> >> >> >> >> work, regardless of what kind of key they have. >>> >> >> >> >> >>> >> >> >> >> > The difference here is that in your use cases, library A is >>> >> >> >> >> > "sneakily" >>> >> >> >> >> > storing information on object B. >>> >> >> >> >> >>> >> >> >> >> What use case are you referring to here? I can’t find any >>> example >>> >> >> >> >> in >>> >> >> >> >> the >>> >> >> >> >> previous posts that matches these descriptions. As Isiah >>> said, >>> >> >> >> >> “all >>> >> >> >> >> of >>> >> >> >> >> the >>> >> >> >> >> examples here I've presented are for scenarios where the >>> state is >>> >> >> >> >> related to >>> >> >> >> >> the factory that created the objects.” The same is true of my >>> >> >> >> >> examples. >>> >> >> >> >> Everybody’s on the same page regarding not wanting to add >>> >> >> >> >> properties >>> >> >> >> >> to >>> >> >> >> >> objects their own libraries do not create. >>> >> _______________________________________________ >>> >> 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/20180803/94c9b579/attachment-0001.html>
On Fri, Aug 3, 2018 at 2:33 PM Ranando King <kingmph at gmail.com> wrote:
A side thought:
If a language reserving certain words, even to the point of generating error messages related to using them under certain circumstances, doesn't constitute at least part of a justification for using them, then why do languages so often reserve keywords for future use? Isn't precisely the case with
private
,protected
,public
, andpackage
(andclass
prior to ES6). Weren't they all holdovers from the fact that the syntax for ES was mostly borrowed from Java, and kept in reserve just in case the concepts behind these keywords became language features?If that's not the case, then there's no point in keeping these (or indeed any) keywords in reserve.
Yes, they were reserved because they were the Java reserved keywords, with the intention that we might add more Java features later in the langauge's evolution. That has no bearing on their use today.
Unreserving them wouldn't provide much benefit now. But keeping them reserved still doesn't mean we have any particular need to use them.
On Fri, Aug 3, 2018 at 2:33 PM Ranando King <kingmph at gmail.com> wrote: > > A side thought: > > If a language reserving certain words, even to the point of generating error messages related to using them under certain circumstances, doesn't constitute at least part of a justification for using them, then why do languages so often reserve keywords for future use? Isn't precisely the case with `private`, `protected`, `public`, and `package` (and `class` prior to ES6). Weren't they all holdovers from the fact that the syntax for ES was mostly borrowed from Java, and kept in reserve just in case the concepts behind these keywords became language features? > > If that's not the case, then there's no point in keeping these (or indeed any) keywords in reserve. Yes, they were reserved because they were the Java reserved keywords, with the intention that we might add more Java features later in the langauge's evolution. That has no bearing on their use today. Unreserving them wouldn't provide much benefit now. But keeping them reserved still doesn't mean we have any particular need to use them. ~TJ
On 08/03/2018 02:37 PM, Tab Atkins Jr. wrote:
Yes, they were reserved because they were the Java reserved keywords, with the intention that we might add more Java features later in the langauge's evolution. That has no bearing on their use today.
That's exactly what we did. In the early days of ECMAScript we had no plans to use those but reserved them just in case. Some of the ones we reserved later accidentally became unreserved.
Introducing a new keyword-based feature when that keyword isn't reserved leads to severe complications and arbitrary [no linebreak here] rules. The nastiest cover grammars are some of the consequences of things like being able to use "async" as a function name.
Waldemar
On 08/03/2018 02:37 PM, Tab Atkins Jr. wrote: > Yes, they were reserved because they were the Java reserved keywords, > with the intention that we might add more Java features later in the > langauge's evolution. That has no bearing on their use today. That's exactly what we did. In the early days of ECMAScript we had no plans to use those but reserved them just in case. Some of the ones we reserved later accidentally became unreserved. Introducing a new keyword-based feature when that keyword isn't reserved leads to severe complications and arbitrary [no linebreak here] rules. The nastiest cover grammars are some of the consequences of things like being able to use "async" as a function name. Waldemar
private
,protected
,class
, and a few other such keywords have all been part of ES since long be for the TC39 board got their hands on it. They hadn't been implemented, but they were all a very real part of ES.
Whoa. Is that just misinformed or intentionally misleading? They have never been "part of ES" in any meaningful sense. It was not that they had not been implemented; it was that they had not even been defined. To say they are a "real part of ES" is a strange interpretation of the meaning of the word "real". The notion that we would choose features to work on based on some designation of certain keywords as reserved long ago, and that they are now "languishing", is odd. Why not focus on "implementing" enum, or final, or throws, or any other of the dozens of reserved words?
Having said that, I think it is a valid general principle that as language
designers we should be very reluctant to use magic characters. **
is
fine, of course, as is =>
, or even @
for decorators. Personally, I
don't think the problem of access modifiers rises to the level of
commonality and need for conciseness that would justify eating up another
magic character. We also don't want JS to start looking like Perl or APL.
Speaking as a self-appointed representative of Plain Old Programmers, I do
feel a need for private fields, although it was probably starting to
program more in TS that got me thinking that way. However, to me it feels
odd to tie this directly to class
syntax. Why can't I have a private
field in a plain old object like {a: 1}
(i.e., that would only be
accessible via a method on that object? We already have properties which
are enumerable and writable, for example, independent of the class
mechanism. Why not have properties which are private in the same way?
The problem,of course, is that even assuming the engines implemented the
private
property on descriptors, I obviously don't want to have to write
Object.create({}, {a: {value: 22, private: true})
. So the problem can be
restated as trying to find some nice sugar for writing the above. You
know, something like {a<private>: 22}
. That's obviously a completely
random syntax suggestion, just to show the idea. Perhaps we'd prefer to
have the access modifiers be specifiable under program control as an object
itself, to allow something like
const PRIVATE = {private: true};
const myObject = {a(<PRIVATE>: 2; }
But what would the precise meaning of such as private
descriptor property
be? In operational terms, it could suffice to imagine (as a behavior, not
as an implementation strategy) that objects would have a flag that would
skip over private properties when doing property lookups. I think the right
implementation is to have a private property look like it's not there at
all when access is attempted from outside the object (in other words, is
undefined), rather than some kind of PrivatePropertyAccessError
.
The above approach ought to be extensible to class notation:
class Foo (
bar<PRIVATE>(): { return 22; }
}
which would end up being something like
Object.defineProperty(Foo.prototype, "bar", {value() {return 22; }, private: true})
.
Or when classes get instance variables:
class Foo {
bar<PRIVATE> = 22;
Was anything along these lines already brought up in this discussion?
Bob
> `private`, `protected`, `class`, and a few other such keywords have all been part of ES since long be for the TC39 board got their hands on it. They hadn't been implemented, but they were all a very real part of ES. Whoa. Is that just misinformed or intentionally misleading? They have never been "part of ES" in any meaningful sense. It was not that they had not been implemented; it was that they had not even been defined. To say they are a "real part of ES" is a strange interpretation of the meaning of the word "real". The notion that we would choose features to work on based on some designation of certain keywords as reserved long ago, and that they are now "languishing", is odd. Why not focus on "implementing" enum, or final, or throws, or any other of the dozens of reserved words? Having said that, I think it is a valid general principle that as language designers we should be very reluctant to use magic characters. `**` is fine, of course, as is `=>`, or even `@` for decorators. Personally, I don't think the problem of access modifiers rises to the level of commonality and need for conciseness that would justify eating up another magic character. We also don't want JS to start looking like Perl or APL. Speaking as a self-appointed representative of Plain Old Programmers, I do feel a need for private fields, although it was probably starting to program more in TS that got me thinking that way. However, to me it feels odd to tie this directly to `class` syntax. Why can't I have a private field in a plain old object like `{a: 1}` (i.e., that would only be accessible via a method on that object? We already have properties which are enumerable and writable, for example, independent of the class mechanism. Why not have properties which are private in the same way? The problem,of course, is that even assuming the engines implemented the `private` property on descriptors, I obviously don't want to have to write `Object.create({}, {a: {value: 22, private: true})`. So the problem can be restated as trying to find some nice sugar for writing the above. You know, something like `{a<private>: 22}`. That's obviously a completely random syntax suggestion, just to show the idea. Perhaps we'd prefer to have the access modifiers be specifiable under program control as an object itself, to allow something like ``` const PRIVATE = {private: true}; const myObject = {a(<PRIVATE>: 2; } ``` But what would the precise meaning of such as `private` descriptor property be? In operational terms, it could suffice to imagine (as a behavior, not as an implementation strategy) that objects would have a flag that would skip over private properties when doing property lookups. I think the right implementation is to have a private property look like it's not there at all when access is attempted from outside the object (in other words, is undefined), rather than some kind of `PrivatePropertyAccessError`. The above approach ought to be extensible to class notation: ``` class Foo ( bar<PRIVATE>(): { return 22; } } ``` which would end up being something like `Object.defineProperty(Foo.prototype, "bar", {value() {return 22; }, private: true})`. Or when classes get instance variables: ``` class Foo { bar<PRIVATE> = 22; ``` Was anything along these lines already brought up in this discussion? Bob On Sat, Aug 4, 2018 at 12:30 AM Ranando King <kingmph at gmail.com> wrote: > > It certainly doesn't look or feel like JS - it feels more like Java or > C#. > > `private`, `protected`, `class`, and a few other such keywords have all > been part of ES since long be for the TC39 board got their hands on it. > They hadn't been implemented, but they were all a very real part of ES. Now > that `class` has been implemented, it makes little sense to leave behind > the `private` and `protected` keywords when we are trying to implement > their functionality. > > > `private` looks like an identifier, and IMHO getters, setters, and > async functions suffer the same issue of the keyword seeming to blend in a > little with surrounding code. > > Have you ever thought that `var` or `let` look like identifiers? The > `private` and `protected` keywords serve the same role as `var` and `let`: > declaring a variable within a given scope or context. If you think there is > a good logical or rational reason to avoid using the keywords that have > been embedded in the language and left languishing, waiting for their > meaning to be implemented, then I'm willing to entertain that. If the > reason is based on mere feeling or emotion, well. I will only entertain > such arguments if my reason for doing things a certain way is equally > emotion based. Nothing I'm aware of in this proposal falls into that > category. I have logical reasons for every choice I've made. > > >> 2. `protected` on an object literal is next to useless. I've used that kind > of feature almost never. > > > And how would that be accessible? > > As you said, the vast majority of the time, this feature will go unused. > However, when it's needed, it would look something like this: > > ```js > var a = { > protected sharedData: 1, > increment() { ++this#.sharedData; }, > print() { console.log(`sharedData = ${this#.sharedData}`); } > }; > > var b = { > __proto__: a, > decrement() { --this#.sharedData; } > }; > ``` > > Setting `b.__proto__ = a` causes `b.[[PrivateValues]].__proto__ = > a.[[PrivateValues]]`, `b.[[DeclarationInfo]].__proto__ = > a.[[InheritanceInfo]]`, and `b.[[InheritanceInfo]].proto = > a.[[InheritanceInfo]]`. So it all just works. > > > I saw `obj#['key']`, which *strongly* suggests dynamic keys are > supported. > > Dynamic **_keys_** are supported. Dynamic **_properties_** are not! Please > don't conflate the two. Dynamic keys are calculated property names. I am > definitely supporting that. Dynamic properties refers to the ability to add > and remove properties from an object at any time. I am not supporting that > for private/protected members (unless someone can logically convince me > it's a good idea). > > On Fri, Aug 3, 2018 at 1:02 PM Isiah Meadows <isiahmeadows at gmail.com> > wrote: > >> Inline >> >> On Fri, Aug 3, 2018, 11:12 Ranando King <kingmph at gmail.com> wrote: >> >>> > 1. It's *super incredibly boilerplatey* and verbose syntactically. >>> >>> I'm not sure what you mean by "boilerplatey". As for being verbose, I'm >>> just using the keywords everyone understands for this purpose. IMO, there's >>> no advantage in trying to find some shorthand to do the same thing just >>> because it saves a keystroke or two when it makes the code significantly >>> more difficult to understand. >>> >> >> But on the same token, it's verbose enough that I feel readability starts >> to suffer substantiallly. `private` looks like an identifier, and IMHO >> getters, setters, and async functions suffer the same issue of the keyword >> seeming to blend in a little with surrounding code. But those are more like >> decorating the function than the name. >> >> Based on reading the several meeting notes, I don't believe the keyword >> has been especially popular there, either. It certainly doesn't look or >> feel like JS - it feels more like Java or C#. >> >> >>> > 2. `protected` on an object literal is next to useless. I've used >>> that kind of feature almost never. >>> >>> I get where you're coming from with that. I don't see it being used very >>> often (kinda like `with`), but it has to be there. If someone wants to use >>> the facilities of `class` without the limitations of the keyword, and the >>> intent is to build vertical hierarchies, they'll need the "protected" >>> keyword on their prototype definition to share private data with descendant >>> factories. >>> >> >> And how would that be accessible? Because you can't expose it via the >> same way you do in classes without basically making them public (and >> several workarounds suffer similar issues). >> >> > I also find it odd you're supporting private dynamic properties. >>> >>> How'd you get to the idea that I'm supporting dynamic private >>> properties? >>> >> >> I saw `obj#['key']`, which *strongly* suggests dynamic keys are supported. >> >> > I actually think it's odd there is no attempt to implement dynamic >>> properties in the other "private properties" proposals. >>> >> >>> It's not that odd. There are issues around inheritance when a subclass >>> can remove the `protected` properties of its base. Further, exactly how do >>> you add a new `protected` property at runtime? Under both >>> proposal-class-fields and proposal-object-members, there is never any >>> direct access to the private container record, so use of >>> `Object.defineProperty` will never work. IMO, any attempt to implement >>> dynamic private properties in any sensible and consistent fashion would >>> require somehow exposing the private data record to the code. That's a >>> recipe for a private data leak. Not worth 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/20180804/79df8551/attachment-0001.html>
I also agree private properties / instance variables should not be class-exclusive.
I was a big fan of the syntax used in the JavaScript Classes 1.1 proposal ( zenparsing/js-classes-1.1#1-instance-variables) and always felt like it could have been painlessly extended to plain old objects.
I also agree private properties / instance variables should not be class-exclusive. I was a big fan of the syntax used in the JavaScript Classes 1.1 proposal ( https://github.com/zenparsing/js-classes-1.1#1-instance-variables) and always felt like it could have been painlessly extended to plain old objects. On Fri, Aug 3, 2018 at 11:30 PM, Bob Myers <rtm at gol.com> wrote: > > `private`, `protected`, `class`, and a few other such keywords have all > been part of ES since long be for the TC39 board got their hands on it. > They hadn't been implemented, but they were all a very real part of ES. > > Whoa. Is that just misinformed or intentionally misleading? They have > never been "part of ES" in any meaningful sense. It was not that they had > not been implemented; it was that they had not even been defined. To say > they are a "real part of ES" is a strange interpretation of the meaning of > the word "real". The notion that we would choose features to work on based > on some designation of certain keywords as reserved long ago, and that they > are now "languishing", is odd. Why not focus on "implementing" enum, or > final, or throws, or any other of the dozens of reserved words? > > Having said that, I think it is a valid general principle that as language > designers we should be very reluctant to use magic characters. `**` is > fine, of course, as is `=>`, or even `@` for decorators. Personally, I > don't think the problem of access modifiers rises to the level of > commonality and need for conciseness that would justify eating up another > magic character. We also don't want JS to start looking like Perl or APL. > > Speaking as a self-appointed representative of Plain Old Programmers, I do > feel a need for private fields, although it was probably starting to > program more in TS that got me thinking that way. However, to me it feels > odd to tie this directly to `class` syntax. Why can't I have a private > field in a plain old object like `{a: 1}` (i.e., that would only be > accessible via a method on that object? We already have properties which > are enumerable and writable, for example, independent of the class > mechanism. Why not have properties which are private in the same way? > > The problem,of course, is that even assuming the engines implemented the > `private` property on descriptors, I obviously don't want to have to write > `Object.create({}, {a: {value: 22, private: true})`. So the problem can be > restated as trying to find some nice sugar for writing the above. You > know, something like `{a<private>: 22}`. That's obviously a completely > random syntax suggestion, just to show the idea. Perhaps we'd prefer to > have the access modifiers be specifiable under program control as an object > itself, to allow something like > > ``` > const PRIVATE = {private: true}; > > const myObject = {a(<PRIVATE>: 2; } > ``` > > But what would the precise meaning of such as `private` descriptor > property be? In operational terms, it could suffice to imagine (as a > behavior, not as an implementation strategy) that objects would have a flag > that would skip over private properties when doing property lookups. I > think the right implementation is to have a private property look like it's > not there at all when access is attempted from outside the object (in other > words, is undefined), rather than some kind of `PrivatePropertyAccessError`. > > The above approach ought to be extensible to class notation: > > ``` > class Foo ( > bar<PRIVATE>(): { return 22; } > } > ``` > > which would end up being something like `Object.defineProperty(Foo.prototype, > "bar", {value() {return 22; }, private: true})`. > > Or when classes get instance variables: > > ``` > class Foo { > bar<PRIVATE> = 22; > ``` > > Was anything along these lines already brought up in this discussion? > > Bob > > > On Sat, Aug 4, 2018 at 12:30 AM Ranando King <kingmph at gmail.com> wrote: > >> > It certainly doesn't look or feel like JS - it feels more like Java or >> C#. >> >> `private`, `protected`, `class`, and a few other such keywords have all >> been part of ES since long be for the TC39 board got their hands on it. >> They hadn't been implemented, but they were all a very real part of ES. Now >> that `class` has been implemented, it makes little sense to leave behind >> the `private` and `protected` keywords when we are trying to implement >> their functionality. >> >> > `private` looks like an identifier, and IMHO getters, setters, and >> async functions suffer the same issue of the keyword seeming to blend in a >> little with surrounding code. >> >> Have you ever thought that `var` or `let` look like identifiers? The >> `private` and `protected` keywords serve the same role as `var` and `let`: >> declaring a variable within a given scope or context. If you think there is >> a good logical or rational reason to avoid using the keywords that have >> been embedded in the language and left languishing, waiting for their >> meaning to be implemented, then I'm willing to entertain that. If the >> reason is based on mere feeling or emotion, well. I will only entertain >> such arguments if my reason for doing things a certain way is equally >> emotion based. Nothing I'm aware of in this proposal falls into that >> category. I have logical reasons for every choice I've made. >> >> >> 2. `protected` on an object literal is next to useless. I've used >> that kind of feature almost never. >> >> > And how would that be accessible? >> >> As you said, the vast majority of the time, this feature will go unused. >> However, when it's needed, it would look something like this: >> >> ```js >> var a = { >> protected sharedData: 1, >> increment() { ++this#.sharedData; }, >> print() { console.log(`sharedData = ${this#.sharedData}`); } >> }; >> >> var b = { >> __proto__: a, >> decrement() { --this#.sharedData; } >> }; >> ``` >> >> Setting `b.__proto__ = a` causes `b.[[PrivateValues]].__proto__ = >> a.[[PrivateValues]]`, `b.[[DeclarationInfo]].__proto__ = >> a.[[InheritanceInfo]]`, and `b.[[InheritanceInfo]].proto = >> a.[[InheritanceInfo]]`. So it all just works. >> >> > I saw `obj#['key']`, which *strongly* suggests dynamic keys are >> supported. >> >> Dynamic **_keys_** are supported. Dynamic **_properties_** are not! >> Please don't conflate the two. Dynamic keys are calculated property names. >> I am definitely supporting that. Dynamic properties refers to the ability >> to add and remove properties from an object at any time. I am not >> supporting that for private/protected members (unless someone can logically >> convince me it's a good idea). >> >> On Fri, Aug 3, 2018 at 1:02 PM Isiah Meadows <isiahmeadows at gmail.com> >> wrote: >> >>> Inline >>> >>> On Fri, Aug 3, 2018, 11:12 Ranando King <kingmph at gmail.com> wrote: >>> >>>> > 1. It's *super incredibly boilerplatey* and verbose syntactically. >>>> >>>> I'm not sure what you mean by "boilerplatey". As for being verbose, I'm >>>> just using the keywords everyone understands for this purpose. IMO, there's >>>> no advantage in trying to find some shorthand to do the same thing just >>>> because it saves a keystroke or two when it makes the code significantly >>>> more difficult to understand. >>>> >>> >>> But on the same token, it's verbose enough that I feel readability >>> starts to suffer substantiallly. `private` looks like an identifier, and >>> IMHO getters, setters, and async functions suffer the same issue of the >>> keyword seeming to blend in a little with surrounding code. But those are >>> more like decorating the function than the name. >>> >>> Based on reading the several meeting notes, I don't believe the keyword >>> has been especially popular there, either. It certainly doesn't look or >>> feel like JS - it feels more like Java or C#. >>> >>> >>>> > 2. `protected` on an object literal is next to useless. I've used >>>> that kind of feature almost never. >>>> >>>> I get where you're coming from with that. I don't see it being used >>>> very often (kinda like `with`), but it has to be there. If someone wants to >>>> use the facilities of `class` without the limitations of the keyword, and >>>> the intent is to build vertical hierarchies, they'll need the "protected" >>>> keyword on their prototype definition to share private data with descendant >>>> factories. >>>> >>> >>> And how would that be accessible? Because you can't expose it via the >>> same way you do in classes without basically making them public (and >>> several workarounds suffer similar issues). >>> >>> > I also find it odd you're supporting private dynamic properties. >>>> >>>> How'd you get to the idea that I'm supporting dynamic private >>>> properties? >>>> >>> >>> I saw `obj#['key']`, which *strongly* suggests dynamic keys are >>> supported. >>> >>> > I actually think it's odd there is no attempt to implement dynamic >>>> properties in the other "private properties" proposals. >>>> >>> >>>> It's not that odd. There are issues around inheritance when a subclass >>>> can remove the `protected` properties of its base. Further, exactly how do >>>> you add a new `protected` property at runtime? Under both >>>> proposal-class-fields and proposal-object-members, there is never any >>>> direct access to the private container record, so use of >>>> `Object.defineProperty` will never work. IMO, any attempt to implement >>>> dynamic private properties in any sensible and consistent fashion would >>>> require somehow exposing the private data record to the code. That's a >>>> recipe for a private data leak. Not worth 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/20180804/5c9e6b19/attachment-0001.html>
You might want to consider it "intentionally misleading", but when I say that they are a "real part of ES", I mean that when they are present under certain circumstances, ES will take on behavior it wouldn't have if the words had not been reserved. Does that mean those words are of any practical use in the language? NO. However, the simple fact that the behavior of the language changes as a result of their appearance makes them a part of the language, albeit useless. However, all this has no logical merit for my reason to use them. So believe what you will. I just wanted to see how people would respond. That gives me a bit of useful information about how to word my proposal as I make adjustments.
You might want to consider it "intentionally misleading", but when I say that they are a "real part of ES", I mean that when they are present under certain circumstances, ES will take on behavior it wouldn't have if the words had not been reserved. Does that mean those words are of any practical use in the language? **NO**. However, the simple fact that the behavior of the language changes as a result of their appearance makes them a part of the language, albeit useless. However, all this has no logical merit for my reason to use them. So believe what you will. I just wanted to see how people would respond. That gives me a bit of useful information about how to word my proposal as I make adjustments. On Fri, Aug 3, 2018 at 10:32 PM Bob Myers <rtm at gol.com> wrote: > > `private`, `protected`, `class`, and a few other such keywords have all > been part of ES since long be for the TC39 board got their hands on it. > They hadn't been implemented, but they were all a very real part of ES. > > Whoa. Is that just misinformed or intentionally misleading? They have > never been "part of ES" in any meaningful sense. It was not that they had > not been implemented; it was that they had not even been defined. To say > they are a "real part of ES" is a strange interpretation of the meaning of > the word "real". The notion that we would choose features to work on based > on some designation of certain keywords as reserved long ago, and that they > are now "languishing", is odd. Why not focus on "implementing" enum, or > final, or throws, or any other of the dozens of reserved words? > > Having said that, I think it is a valid general principle that as language > designers we should be very reluctant to use magic characters. `**` is > fine, of course, as is `=>`, or even `@` for decorators. Personally, I > don't think the problem of access modifiers rises to the level of > commonality and need for conciseness that would justify eating up another > magic character. We also don't want JS to start looking like Perl or APL. > > Speaking as a self-appointed representative of Plain Old Programmers, I do > feel a need for private fields, although it was probably starting to > program more in TS that got me thinking that way. However, to me it feels > odd to tie this directly to `class` syntax. Why can't I have a private > field in a plain old object like `{a: 1}` (i.e., that would only be > accessible via a method on that object? We already have properties which > are enumerable and writable, for example, independent of the class > mechanism. Why not have properties which are private in the same way? > > The problem,of course, is that even assuming the engines implemented the > `private` property on descriptors, I obviously don't want to have to write > `Object.create({}, {a: {value: 22, private: true})`. So the problem can be > restated as trying to find some nice sugar for writing the above. You > know, something like `{a<private>: 22}`. That's obviously a completely > random syntax suggestion, just to show the idea. Perhaps we'd prefer to > have the access modifiers be specifiable under program control as an object > itself, to allow something like > > ``` > const PRIVATE = {private: true}; > > const myObject = {a(<PRIVATE>: 2; } > ``` > > But what would the precise meaning of such as `private` descriptor > property be? In operational terms, it could suffice to imagine (as a > behavior, not as an implementation strategy) that objects would have a flag > that would skip over private properties when doing property lookups. I > think the right implementation is to have a private property look like it's > not there at all when access is attempted from outside the object (in other > words, is undefined), rather than some kind of `PrivatePropertyAccessError`. > > The above approach ought to be extensible to class notation: > > ``` > class Foo ( > bar<PRIVATE>(): { return 22; } > } > ``` > > which would end up being something like > `Object.defineProperty(Foo.prototype, "bar", {value() {return 22; }, > private: true})`. > > Or when classes get instance variables: > > ``` > class Foo { > bar<PRIVATE> = 22; > ``` > > Was anything along these lines already brought up in this discussion? > > Bob > > > On Sat, Aug 4, 2018 at 12:30 AM Ranando King <kingmph at gmail.com> wrote: > >> > It certainly doesn't look or feel like JS - it feels more like Java or >> C#. >> >> `private`, `protected`, `class`, and a few other such keywords have all >> been part of ES since long be for the TC39 board got their hands on it. >> They hadn't been implemented, but they were all a very real part of ES. Now >> that `class` has been implemented, it makes little sense to leave behind >> the `private` and `protected` keywords when we are trying to implement >> their functionality. >> >> > `private` looks like an identifier, and IMHO getters, setters, and >> async functions suffer the same issue of the keyword seeming to blend in a >> little with surrounding code. >> >> Have you ever thought that `var` or `let` look like identifiers? The >> `private` and `protected` keywords serve the same role as `var` and `let`: >> declaring a variable within a given scope or context. If you think there is >> a good logical or rational reason to avoid using the keywords that have >> been embedded in the language and left languishing, waiting for their >> meaning to be implemented, then I'm willing to entertain that. If the >> reason is based on mere feeling or emotion, well. I will only entertain >> such arguments if my reason for doing things a certain way is equally >> emotion based. Nothing I'm aware of in this proposal falls into that >> category. I have logical reasons for every choice I've made. >> >> >> 2. `protected` on an object literal is next to useless. I've used >> that kind of feature almost never. >> >> > And how would that be accessible? >> >> As you said, the vast majority of the time, this feature will go unused. >> However, when it's needed, it would look something like this: >> >> ```js >> var a = { >> protected sharedData: 1, >> increment() { ++this#.sharedData; }, >> print() { console.log(`sharedData = ${this#.sharedData}`); } >> }; >> >> var b = { >> __proto__: a, >> decrement() { --this#.sharedData; } >> }; >> ``` >> >> Setting `b.__proto__ = a` causes `b.[[PrivateValues]].__proto__ = >> a.[[PrivateValues]]`, `b.[[DeclarationInfo]].__proto__ = >> a.[[InheritanceInfo]]`, and `b.[[InheritanceInfo]].proto = >> a.[[InheritanceInfo]]`. So it all just works. >> >> > I saw `obj#['key']`, which *strongly* suggests dynamic keys are >> supported. >> >> Dynamic **_keys_** are supported. Dynamic **_properties_** are not! >> Please don't conflate the two. Dynamic keys are calculated property names. >> I am definitely supporting that. Dynamic properties refers to the ability >> to add and remove properties from an object at any time. I am not >> supporting that for private/protected members (unless someone can logically >> convince me it's a good idea). >> >> On Fri, Aug 3, 2018 at 1:02 PM Isiah Meadows <isiahmeadows at gmail.com> >> wrote: >> >>> Inline >>> >>> On Fri, Aug 3, 2018, 11:12 Ranando King <kingmph at gmail.com> wrote: >>> >>>> > 1. It's *super incredibly boilerplatey* and verbose syntactically. >>>> >>>> I'm not sure what you mean by "boilerplatey". As for being verbose, I'm >>>> just using the keywords everyone understands for this purpose. IMO, there's >>>> no advantage in trying to find some shorthand to do the same thing just >>>> because it saves a keystroke or two when it makes the code significantly >>>> more difficult to understand. >>>> >>> >>> But on the same token, it's verbose enough that I feel readability >>> starts to suffer substantiallly. `private` looks like an identifier, and >>> IMHO getters, setters, and async functions suffer the same issue of the >>> keyword seeming to blend in a little with surrounding code. But those are >>> more like decorating the function than the name. >>> >>> Based on reading the several meeting notes, I don't believe the keyword >>> has been especially popular there, either. It certainly doesn't look or >>> feel like JS - it feels more like Java or C#. >>> >>> >>>> > 2. `protected` on an object literal is next to useless. I've used >>>> that kind of feature almost never. >>>> >>>> I get where you're coming from with that. I don't see it being used >>>> very often (kinda like `with`), but it has to be there. If someone wants to >>>> use the facilities of `class` without the limitations of the keyword, and >>>> the intent is to build vertical hierarchies, they'll need the "protected" >>>> keyword on their prototype definition to share private data with descendant >>>> factories. >>>> >>> >>> And how would that be accessible? Because you can't expose it via the >>> same way you do in classes without basically making them public (and >>> several workarounds suffer similar issues). >>> >>> > I also find it odd you're supporting private dynamic properties. >>>> >>>> How'd you get to the idea that I'm supporting dynamic private >>>> properties? >>>> >>> >>> I saw `obj#['key']`, which *strongly* suggests dynamic keys are >>> supported. >>> >>> > I actually think it's odd there is no attempt to implement dynamic >>>> properties in the other "private properties" proposals. >>>> >>> >>>> It's not that odd. There are issues around inheritance when a subclass >>>> can remove the `protected` properties of its base. Further, exactly how do >>>> you add a new `protected` property at runtime? Under both >>>> proposal-class-fields and proposal-object-members, there is never any >>>> direct access to the private container record, so use of >>>> `Object.defineProperty` will never work. IMO, any attempt to implement >>>> dynamic private properties in any sensible and consistent fashion would >>>> require somehow exposing the private data record to the code. That's a >>>> recipe for a private data leak. Not worth 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/20180804/48b5e07c/attachment-0001.html>
to Bob Myers: This is exactly the kind of private both proposal-class-fields and proposal-object-members is proposing. The main differences between the two are the syntax and the fact that mine also allows such fields to be defined directly on objects.
to Michael Theriot: Turns out that if the implementation is sound, the same mechanism that works for class declarations also works for object declarations. The only extra bit required is that the language parser has to recognize the feature.
to Bob Myers: This is exactly the kind of private both proposal-class-fields and proposal-object-members is proposing. The main differences between the two are the syntax and the fact that mine also allows such fields to be defined directly on objects. to Michael Theriot: Turns out that if the implementation is sound, the same mechanism that works for class declarations also works for object declarations. The only extra bit required is that the language parser has to recognize the feature. On Sat, Aug 4, 2018 at 2:29 PM Michael Theriot < michael.lee.theriot at gmail.com> wrote: > I also agree private properties / instance variables should not be > class-exclusive. > > I was a big fan of the syntax used in the JavaScript Classes 1.1 proposal ( > https://github.com/zenparsing/js-classes-1.1#1-instance-variables) and > always felt like it could have been painlessly extended to plain old > objects. > > On Fri, Aug 3, 2018 at 11:30 PM, Bob Myers <rtm at gol.com> wrote: > >> > `private`, `protected`, `class`, and a few other such keywords have >> all been part of ES since long be for the TC39 board got their hands on >> it. They hadn't been implemented, but they were all a very real part of >> ES. >> >> Whoa. Is that just misinformed or intentionally misleading? They have >> never been "part of ES" in any meaningful sense. It was not that they had >> not been implemented; it was that they had not even been defined. To say >> they are a "real part of ES" is a strange interpretation of the meaning of >> the word "real". The notion that we would choose features to work on based >> on some designation of certain keywords as reserved long ago, and that they >> are now "languishing", is odd. Why not focus on "implementing" enum, or >> final, or throws, or any other of the dozens of reserved words? >> >> Having said that, I think it is a valid general principle that as >> language designers we should be very reluctant to use magic characters. >> `**` is fine, of course, as is `=>`, or even `@` for decorators. >> Personally, I don't think the problem of access modifiers rises to the >> level of commonality and need for conciseness that would justify eating up >> another magic character. We also don't want JS to start looking like Perl >> or APL. >> >> Speaking as a self-appointed representative of Plain Old Programmers, I >> do feel a need for private fields, although it was probably starting to >> program more in TS that got me thinking that way. However, to me it feels >> odd to tie this directly to `class` syntax. Why can't I have a private >> field in a plain old object like `{a: 1}` (i.e., that would only be >> accessible via a method on that object? We already have properties which >> are enumerable and writable, for example, independent of the class >> mechanism. Why not have properties which are private in the same way? >> >> The problem,of course, is that even assuming the engines implemented the >> `private` property on descriptors, I obviously don't want to have to write >> `Object.create({}, {a: {value: 22, private: true})`. So the problem can be >> restated as trying to find some nice sugar for writing the above. You >> know, something like `{a<private>: 22}`. That's obviously a completely >> random syntax suggestion, just to show the idea. Perhaps we'd prefer to >> have the access modifiers be specifiable under program control as an object >> itself, to allow something like >> >> ``` >> const PRIVATE = {private: true}; >> >> const myObject = {a(<PRIVATE>: 2; } >> ``` >> >> But what would the precise meaning of such as `private` descriptor >> property be? In operational terms, it could suffice to imagine (as a >> behavior, not as an implementation strategy) that objects would have a flag >> that would skip over private properties when doing property lookups. I >> think the right implementation is to have a private property look like it's >> not there at all when access is attempted from outside the object (in other >> words, is undefined), rather than some kind of `PrivatePropertyAccessError`. >> >> The above approach ought to be extensible to class notation: >> >> ``` >> class Foo ( >> bar<PRIVATE>(): { return 22; } >> } >> ``` >> >> which would end up being something like >> `Object.defineProperty(Foo.prototype, "bar", {value() {return 22; }, >> private: true})`. >> >> Or when classes get instance variables: >> >> ``` >> class Foo { >> bar<PRIVATE> = 22; >> ``` >> >> Was anything along these lines already brought up in this discussion? >> >> Bob >> >> >> On Sat, Aug 4, 2018 at 12:30 AM Ranando King <kingmph at gmail.com> wrote: >> >>> > It certainly doesn't look or feel like JS - it feels more like Java >>> or C#. >>> >>> `private`, `protected`, `class`, and a few other such keywords have all >>> been part of ES since long be for the TC39 board got their hands on it. >>> They hadn't been implemented, but they were all a very real part of ES. Now >>> that `class` has been implemented, it makes little sense to leave behind >>> the `private` and `protected` keywords when we are trying to implement >>> their functionality. >>> >>> > `private` looks like an identifier, and IMHO getters, setters, and >>> async functions suffer the same issue of the keyword seeming to blend in a >>> little with surrounding code. >>> >>> Have you ever thought that `var` or `let` look like identifiers? The >>> `private` and `protected` keywords serve the same role as `var` and `let`: >>> declaring a variable within a given scope or context. If you think there is >>> a good logical or rational reason to avoid using the keywords that have >>> been embedded in the language and left languishing, waiting for their >>> meaning to be implemented, then I'm willing to entertain that. If the >>> reason is based on mere feeling or emotion, well. I will only entertain >>> such arguments if my reason for doing things a certain way is equally >>> emotion based. Nothing I'm aware of in this proposal falls into that >>> category. I have logical reasons for every choice I've made. >>> >>> >> 2. `protected` on an object literal is next to useless. I've used >>> that kind of feature almost never. >>> >>> > And how would that be accessible? >>> >>> As you said, the vast majority of the time, this feature will go unused. >>> However, when it's needed, it would look something like this: >>> >>> ```js >>> var a = { >>> protected sharedData: 1, >>> increment() { ++this#.sharedData; }, >>> print() { console.log(`sharedData = ${this#.sharedData}`); } >>> }; >>> >>> var b = { >>> __proto__: a, >>> decrement() { --this#.sharedData; } >>> }; >>> ``` >>> >>> Setting `b.__proto__ = a` causes `b.[[PrivateValues]].__proto__ = >>> a.[[PrivateValues]]`, `b.[[DeclarationInfo]].__proto__ = >>> a.[[InheritanceInfo]]`, and `b.[[InheritanceInfo]].proto = >>> a.[[InheritanceInfo]]`. So it all just works. >>> >>> > I saw `obj#['key']`, which *strongly* suggests dynamic keys are >>> supported. >>> >>> Dynamic **_keys_** are supported. Dynamic **_properties_** are not! >>> Please don't conflate the two. Dynamic keys are calculated property names. >>> I am definitely supporting that. Dynamic properties refers to the ability >>> to add and remove properties from an object at any time. I am not >>> supporting that for private/protected members (unless someone can logically >>> convince me it's a good idea). >>> >>> On Fri, Aug 3, 2018 at 1:02 PM Isiah Meadows <isiahmeadows at gmail.com> >>> wrote: >>> >>>> Inline >>>> >>>> On Fri, Aug 3, 2018, 11:12 Ranando King <kingmph at gmail.com> wrote: >>>> >>>>> > 1. It's *super incredibly boilerplatey* and verbose syntactically. >>>>> >>>>> I'm not sure what you mean by "boilerplatey". As for being verbose, >>>>> I'm just using the keywords everyone understands for this purpose. IMO, >>>>> there's no advantage in trying to find some shorthand to do the same thing >>>>> just because it saves a keystroke or two when it makes the code >>>>> significantly more difficult to understand. >>>>> >>>> >>>> But on the same token, it's verbose enough that I feel readability >>>> starts to suffer substantiallly. `private` looks like an identifier, and >>>> IMHO getters, setters, and async functions suffer the same issue of the >>>> keyword seeming to blend in a little with surrounding code. But those are >>>> more like decorating the function than the name. >>>> >>>> Based on reading the several meeting notes, I don't believe the keyword >>>> has been especially popular there, either. It certainly doesn't look or >>>> feel like JS - it feels more like Java or C#. >>>> >>>> >>>>> > 2. `protected` on an object literal is next to useless. I've used >>>>> that kind of feature almost never. >>>>> >>>>> I get where you're coming from with that. I don't see it being used >>>>> very often (kinda like `with`), but it has to be there. If someone wants to >>>>> use the facilities of `class` without the limitations of the keyword, and >>>>> the intent is to build vertical hierarchies, they'll need the "protected" >>>>> keyword on their prototype definition to share private data with descendant >>>>> factories. >>>>> >>>> >>>> And how would that be accessible? Because you can't expose it via the >>>> same way you do in classes without basically making them public (and >>>> several workarounds suffer similar issues). >>>> >>>> > I also find it odd you're supporting private dynamic properties. >>>>> >>>>> How'd you get to the idea that I'm supporting dynamic private >>>>> properties? >>>>> >>>> >>>> I saw `obj#['key']`, which *strongly* suggests dynamic keys are >>>> supported. >>>> >>>> > I actually think it's odd there is no attempt to implement dynamic >>>>> properties in the other "private properties" proposals. >>>>> >>>> >>>>> It's not that odd. There are issues around inheritance when a subclass >>>>> can remove the `protected` properties of its base. Further, exactly how do >>>>> you add a new `protected` property at runtime? Under both >>>>> proposal-class-fields and proposal-object-members, there is never any >>>>> direct access to the private container record, so use of >>>>> `Object.defineProperty` will never work. IMO, any attempt to implement >>>>> dynamic private properties in any sensible and consistent fashion would >>>>> require somehow exposing the private data record to the code. That's a >>>>> recipe for a private data leak. Not worth 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 >> >> > _______________________________________________ > 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/20180804/52447bcc/attachment-0001.html>
to Bob: Minor correction.
One other thing both proposals agree on is that dynamic addition of private properties is a bad idea. So the use of Object.defineProperty() is simply a non-starter.
to Bob: Minor correction. One other thing both proposals agree on is that dynamic addition of private properties is a bad idea. So the use of Object.defineProperty() is simply a non-starter. On Sat, Aug 4, 2018 at 2:40 PM Ranando King <kingmph at gmail.com> wrote: > to Bob Myers: > This is exactly the kind of private both proposal-class-fields and > proposal-object-members is proposing. The main differences between the two > are the syntax and the fact that mine also allows such fields to be defined > directly on objects. > > to Michael Theriot: > Turns out that if the implementation is sound, the same mechanism that > works for class declarations also works for object declarations. The only > extra bit required is that the language parser has to recognize the feature. > > On Sat, Aug 4, 2018 at 2:29 PM Michael Theriot < > michael.lee.theriot at gmail.com> wrote: > >> I also agree private properties / instance variables should not be >> class-exclusive. >> >> I was a big fan of the syntax used in the JavaScript Classes 1.1 proposal >> (https://github.com/zenparsing/js-classes-1.1#1-instance-variables) and >> always felt like it could have been painlessly extended to plain old >> objects. >> >> On Fri, Aug 3, 2018 at 11:30 PM, Bob Myers <rtm at gol.com> wrote: >> >>> > `private`, `protected`, `class`, and a few other such keywords have >>> all been part of ES since long be for the TC39 board got their hands on >>> it. They hadn't been implemented, but they were all a very real part of >>> ES. >>> >>> Whoa. Is that just misinformed or intentionally misleading? They have >>> never been "part of ES" in any meaningful sense. It was not that they had >>> not been implemented; it was that they had not even been defined. To say >>> they are a "real part of ES" is a strange interpretation of the meaning of >>> the word "real". The notion that we would choose features to work on based >>> on some designation of certain keywords as reserved long ago, and that they >>> are now "languishing", is odd. Why not focus on "implementing" enum, or >>> final, or throws, or any other of the dozens of reserved words? >>> >>> Having said that, I think it is a valid general principle that as >>> language designers we should be very reluctant to use magic characters. >>> `**` is fine, of course, as is `=>`, or even `@` for decorators. >>> Personally, I don't think the problem of access modifiers rises to the >>> level of commonality and need for conciseness that would justify eating up >>> another magic character. We also don't want JS to start looking like Perl >>> or APL. >>> >>> Speaking as a self-appointed representative of Plain Old Programmers, I >>> do feel a need for private fields, although it was probably starting to >>> program more in TS that got me thinking that way. However, to me it feels >>> odd to tie this directly to `class` syntax. Why can't I have a private >>> field in a plain old object like `{a: 1}` (i.e., that would only be >>> accessible via a method on that object? We already have properties which >>> are enumerable and writable, for example, independent of the class >>> mechanism. Why not have properties which are private in the same way? >>> >>> The problem,of course, is that even assuming the engines implemented the >>> `private` property on descriptors, I obviously don't want to have to write >>> `Object.create({}, {a: {value: 22, private: true})`. So the problem can be >>> restated as trying to find some nice sugar for writing the above. You >>> know, something like `{a<private>: 22}`. That's obviously a completely >>> random syntax suggestion, just to show the idea. Perhaps we'd prefer to >>> have the access modifiers be specifiable under program control as an object >>> itself, to allow something like >>> >>> ``` >>> const PRIVATE = {private: true}; >>> >>> const myObject = {a(<PRIVATE>: 2; } >>> ``` >>> >>> But what would the precise meaning of such as `private` descriptor >>> property be? In operational terms, it could suffice to imagine (as a >>> behavior, not as an implementation strategy) that objects would have a flag >>> that would skip over private properties when doing property lookups. I >>> think the right implementation is to have a private property look like it's >>> not there at all when access is attempted from outside the object (in other >>> words, is undefined), rather than some kind of `PrivatePropertyAccessError`. >>> >>> The above approach ought to be extensible to class notation: >>> >>> ``` >>> class Foo ( >>> bar<PRIVATE>(): { return 22; } >>> } >>> ``` >>> >>> which would end up being something like >>> `Object.defineProperty(Foo.prototype, "bar", {value() {return 22; }, >>> private: true})`. >>> >>> Or when classes get instance variables: >>> >>> ``` >>> class Foo { >>> bar<PRIVATE> = 22; >>> ``` >>> >>> Was anything along these lines already brought up in this discussion? >>> >>> Bob >>> >>> >>> On Sat, Aug 4, 2018 at 12:30 AM Ranando King <kingmph at gmail.com> wrote: >>> >>>> > It certainly doesn't look or feel like JS - it feels more like Java >>>> or C#. >>>> >>>> `private`, `protected`, `class`, and a few other such keywords have all >>>> been part of ES since long be for the TC39 board got their hands on it. >>>> They hadn't been implemented, but they were all a very real part of ES. Now >>>> that `class` has been implemented, it makes little sense to leave behind >>>> the `private` and `protected` keywords when we are trying to implement >>>> their functionality. >>>> >>>> > `private` looks like an identifier, and IMHO getters, setters, and >>>> async functions suffer the same issue of the keyword seeming to blend in a >>>> little with surrounding code. >>>> >>>> Have you ever thought that `var` or `let` look like identifiers? The >>>> `private` and `protected` keywords serve the same role as `var` and `let`: >>>> declaring a variable within a given scope or context. If you think there is >>>> a good logical or rational reason to avoid using the keywords that have >>>> been embedded in the language and left languishing, waiting for their >>>> meaning to be implemented, then I'm willing to entertain that. If the >>>> reason is based on mere feeling or emotion, well. I will only entertain >>>> such arguments if my reason for doing things a certain way is equally >>>> emotion based. Nothing I'm aware of in this proposal falls into that >>>> category. I have logical reasons for every choice I've made. >>>> >>>> >> 2. `protected` on an object literal is next to useless. I've used >>>> that kind of feature almost never. >>>> >>>> > And how would that be accessible? >>>> >>>> As you said, the vast majority of the time, this feature will go >>>> unused. However, when it's needed, it would look something like this: >>>> >>>> ```js >>>> var a = { >>>> protected sharedData: 1, >>>> increment() { ++this#.sharedData; }, >>>> print() { console.log(`sharedData = ${this#.sharedData}`); } >>>> }; >>>> >>>> var b = { >>>> __proto__: a, >>>> decrement() { --this#.sharedData; } >>>> }; >>>> ``` >>>> >>>> Setting `b.__proto__ = a` causes `b.[[PrivateValues]].__proto__ = >>>> a.[[PrivateValues]]`, `b.[[DeclarationInfo]].__proto__ = >>>> a.[[InheritanceInfo]]`, and `b.[[InheritanceInfo]].proto = >>>> a.[[InheritanceInfo]]`. So it all just works. >>>> >>>> > I saw `obj#['key']`, which *strongly* suggests dynamic keys are >>>> supported. >>>> >>>> Dynamic **_keys_** are supported. Dynamic **_properties_** are not! >>>> Please don't conflate the two. Dynamic keys are calculated property names. >>>> I am definitely supporting that. Dynamic properties refers to the ability >>>> to add and remove properties from an object at any time. I am not >>>> supporting that for private/protected members (unless someone can logically >>>> convince me it's a good idea). >>>> >>>> On Fri, Aug 3, 2018 at 1:02 PM Isiah Meadows <isiahmeadows at gmail.com> >>>> wrote: >>>> >>>>> Inline >>>>> >>>>> On Fri, Aug 3, 2018, 11:12 Ranando King <kingmph at gmail.com> wrote: >>>>> >>>>>> > 1. It's *super incredibly boilerplatey* and verbose syntactically. >>>>>> >>>>>> I'm not sure what you mean by "boilerplatey". As for being verbose, >>>>>> I'm just using the keywords everyone understands for this purpose. IMO, >>>>>> there's no advantage in trying to find some shorthand to do the same thing >>>>>> just because it saves a keystroke or two when it makes the code >>>>>> significantly more difficult to understand. >>>>>> >>>>> >>>>> But on the same token, it's verbose enough that I feel readability >>>>> starts to suffer substantiallly. `private` looks like an identifier, and >>>>> IMHO getters, setters, and async functions suffer the same issue of the >>>>> keyword seeming to blend in a little with surrounding code. But those are >>>>> more like decorating the function than the name. >>>>> >>>>> Based on reading the several meeting notes, I don't believe the >>>>> keyword has been especially popular there, either. It certainly doesn't >>>>> look or feel like JS - it feels more like Java or C#. >>>>> >>>>> >>>>>> > 2. `protected` on an object literal is next to useless. I've used >>>>>> that kind of feature almost never. >>>>>> >>>>>> I get where you're coming from with that. I don't see it being used >>>>>> very often (kinda like `with`), but it has to be there. If someone wants to >>>>>> use the facilities of `class` without the limitations of the keyword, and >>>>>> the intent is to build vertical hierarchies, they'll need the "protected" >>>>>> keyword on their prototype definition to share private data with descendant >>>>>> factories. >>>>>> >>>>> >>>>> And how would that be accessible? Because you can't expose it via the >>>>> same way you do in classes without basically making them public (and >>>>> several workarounds suffer similar issues). >>>>> >>>>> > I also find it odd you're supporting private dynamic properties. >>>>>> >>>>>> How'd you get to the idea that I'm supporting dynamic private >>>>>> properties? >>>>>> >>>>> >>>>> I saw `obj#['key']`, which *strongly* suggests dynamic keys are >>>>> supported. >>>>> >>>>> > I actually think it's odd there is no attempt to implement dynamic >>>>>> properties in the other "private properties" proposals. >>>>>> >>>>> >>>>>> It's not that odd. There are issues around inheritance when a >>>>>> subclass can remove the `protected` properties of its base. Further, >>>>>> exactly how do you add a new `protected` property at runtime? Under both >>>>>> proposal-class-fields and proposal-object-members, there is never any >>>>>> direct access to the private container record, so use of >>>>>> `Object.defineProperty` will never work. IMO, any attempt to implement >>>>>> dynamic private properties in any sensible and consistent fashion would >>>>>> require somehow exposing the private data record to the code. That's a >>>>>> recipe for a private data leak. Not worth 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 >>> >>> >> _______________________________________________ >> 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/20180804/d038e408/attachment-0001.html>
Just released the POC code for review. Using the Privacy()
export, you
can now wrap any class or object definition and add private
and
protected
members. To add privileged members to a class
, declare a
function static [Privacy.Data]() {}
and return an object containing your
public
data and private
/protected
members. To add privileged members
to any object, declare the field name with array notation, including the
privilege level in the name. The "internal" privilege level has been left
out of this POC and will be released as a separate proposal if this
proposal gains any traction.
Just released the POC code for review. Using the `Privacy()` export, you can now wrap any class or object definition and add `private` and `protected` members. To add privileged members to a `class`, declare a function `static [Privacy.Data]() {}` and return an object containing your `public` data and `private`/`protected` members. To add privileged members to any object, declare the field name with array notation, including the privilege level in the name. The "internal" privilege level has been left out of this POC and will be released as a separate proposal if this proposal gains any traction. -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180805/727cd71e/attachment.html>
On 08/03/2018 08:30 PM, Bob Myers wrote:
private
,protected
,class
, and a few other such keywords have all been part of ES since long be for the TC39 board got their hands on it. They hadn't been implemented, but they were all a very real part of ES.Whoa. Is that just misinformed or intentionally misleading? They have never been "part of ES" in any meaningful sense. It was not that they had not been implemented; it was that they had not even been defined. To say they are a "real part of ES" is a strange interpretation of the meaning of the word "real". The notion that we would choose features to work on based on some designation of certain keywords as reserved long ago, and that they are now "languishing", is odd. Why not focus on "implementing" enum, or final, or throws, or any other of the dozens of reserved words?
Having said that, I think it is a valid general principle that as language designers we should be very reluctant to use magic characters.
**
is fine, of course, as is=>
, or even@
for decorators. Personally, I don't think the problem of access modifiers rises to the level of commonality and need for conciseness that would justify eating up another magic character. We also don't want JS to start looking like Perl or APL.Speaking as a self-appointed representative of Plain Old Programmers, I do feel a need for private fields, although it was probably starting to program more in TS that got me thinking that way. However, to me it feels odd to tie this directly to
class
syntax. Why can't I have a private field in a plain old object like{a: 1}
(i.e., that would only be accessible via a method on that object? We already have properties which are enumerable and writable, for example, independent of the class mechanism. Why not have properties which are private in the same way?The problem,of course, is that even assuming the engines implemented the
private
property on descriptors, I obviously don't want to have to writeObject.create({}, {a: {value: 22, private: true})
. So the problem can be restated as trying to find some nice sugar for writing the above. You know, something like{a<private>: 22}
. That's obviously a completely random syntax suggestion, just to show the idea. Perhaps we'd prefer to have the access modifiers be specifiable under program control as an object itself, to allow something likeconst PRIVATE = {private: true}; const myObject = {a(<PRIVATE>: 2; }
But what would the precise meaning of such as
private
descriptor property be? In operational terms, it could suffice to imagine (as a behavior, not as an implementation strategy) that objects would have a flag that would skip over private properties when doing property lookups. I think the right implementation is to have a private property look like it's not there at all when access is attempted from outside the object (in other words, is undefined), rather than some kind ofPrivatePropertyAccessError
.The above approach ought to be extensible to class notation:
class Foo ( bar<PRIVATE>(): { return 22; } }
which would end up being something like
Object.defineProperty(Foo.prototype, "bar", {value() {return 22; }, private: true})
.Or when classes get instance variables:
class Foo { bar<PRIVATE> = 22;
Was anything along these lines already brought up in this discussion?
Yes. There are a couple answers:
-
If you have the <PRIVATE> marker on both definitions and accesses of the property, then you get a proposal that's essentially isomorphic to the committee's current private proposal.
-
If you have the <PRIVATE> marker on definitions but not accesses of the property, then the proposal leaks private state like a sieve:
For example:
class Foo ( x<PRIVATE>;
SetX() {this.x = <some secret>;} }
To discover what's written into the supposedly private x, just call SetX on an object that's not an instance of Foo.
There are analogous examples for reading instead of writing.
Waldemar
On 08/03/2018 08:30 PM, Bob Myers wrote: > > `private`, `protected`, `class`, and a few other such keywords have all been part of ES since long be for the TC39 board got their hands on it. They hadn't been implemented, but they were all a very real part of ES. > > Whoa. Is that just misinformed or intentionally misleading? They have never been "part of ES" in any meaningful sense. It was not that they had not been implemented; it was that they had not even been defined. To say they are a "real part of ES" is a strange interpretation of the meaning of the word "real". The notion that we would choose features to work on based on some designation of certain keywords as reserved long ago, and that they are now "languishing", is odd. Why not focus on "implementing" enum, or final, or throws, or any other of the dozens of reserved words? > > Having said that, I think it is a valid general principle that as language designers we should be very reluctant to use magic characters. `**` is fine, of course, as is `=>`, or even `@` for decorators. Personally, I don't think the problem of access modifiers rises to the level of commonality and need for conciseness that would justify eating up another magic character. We also don't want JS to start looking like Perl or APL. > > Speaking as a self-appointed representative of Plain Old Programmers, I do feel a need for private fields, although it was probably starting to program more in TS that got me thinking that way. However, to me it feels odd to tie this directly to `class` syntax. Why can't I have a private field in a plain old object like `{a: 1}` (i.e., that would only be accessible via a method on that object? We already have properties which are enumerable and writable, for example, independent of the class mechanism. Why not have properties which are private in the same way? > > The problem,of course, is that even assuming the engines implemented the `private` property on descriptors, I obviously don't want to have to write `Object.create({}, {a: {value: 22, private: true})`. So the problem can be restated as trying to find some nice sugar for writing the above. You know, something like `{a<private>: 22}`. That's obviously a completely random syntax suggestion, just to show the idea. Perhaps we'd prefer to have the access modifiers be specifiable under program control as an object itself, to allow something like > > ``` > const PRIVATE = {private: true}; > > const myObject = {a(<PRIVATE>: 2; } > ``` > > But what would the precise meaning of such as `private` descriptor property be? In operational terms, it could suffice to imagine (as a behavior, not as an implementation strategy) that objects would have a flag that would skip over private properties when doing property lookups. I think the right implementation is to have a private property look like it's not there at all when access is attempted from outside the object (in other words, is undefined), rather than some kind of `PrivatePropertyAccessError`. > > The above approach ought to be extensible to class notation: > > ``` > class Foo ( > bar<PRIVATE>(): { return 22; } > } > ``` > > which would end up being something like `Object.defineProperty(Foo.prototype, "bar", {value() {return 22; }, private: true})`. > > Or when classes get instance variables: > > ``` > class Foo { > bar<PRIVATE> = 22; > ``` > > Was anything along these lines already brought up in this discussion? Yes. There are a couple answers: - If you have the <PRIVATE> marker on both definitions and accesses of the property, then you get a proposal that's essentially isomorphic to the committee's current private proposal. - If you have the <PRIVATE> marker on definitions but not accesses of the property, then the proposal leaks private state like a sieve: For example: class Foo ( x<PRIVATE>; SetX() {this.x = <some secret>;} } To discover what's written into the supposedly private x, just call SetX on an object that's not an instance of Foo. There are analogous examples for reading instead of writing. Waldemar
To discover what's written into the supposedly private x, just call SetX
on an object that's not an instance of Foo.
Actually, it's fairly trivial to avoid that issue in the engine. As long as both objects and functions know what private data they have access to, then all you need is a check to determine whether or not the object instance has access to the same private data as the function. If not, throw a TypeError et voilà, no leaks. That's part of how my POC works. The real difficulty with not having a <PRIVATE> marker on the access comes only because JS
programmers are so very used to being able to tack a new property on to any object they see, and readily abuse this capability to amazing effect. Given that only the creator of an object should be able to manage its private properties, and that these properties must always exist even if undefined (they're considered implementation details), property access quickly becomes a minefield as objects with private members pass through code intent on attaching new public members, even if they're just temporary.
> To discover what's written into the supposedly private x, just call SetX on an object that's not an instance of Foo. Actually, it's fairly trivial to avoid that issue in the engine. As long as both objects and functions know what private data they have access to, then all you need is a check to determine whether or not the object instance has access to the same private data as the function. If not, throw a TypeError et voilà, no leaks. That's part of how my POC works. The real difficulty with not having a <PRIVATE> marker on the access comes only because JS programmers are so very used to being able to tack a new property on to any object they see, and readily abuse this capability to amazing effect. Given that only the creator of an object should be able to manage its private properties, and that these properties must always exist even if undefined (they're considered implementation details), property access quickly becomes a minefield as objects with private members pass through code intent on attaching new public members, even if they're just temporary. On Tue, Aug 7, 2018 at 4:29 PM Waldemar Horwat <waldemar at google.com> wrote: > On 08/03/2018 08:30 PM, Bob Myers wrote: > > > `private`, `protected`, `class`, and a few other such keywords have > all been part of ES since long be for the TC39 board got their hands on it. > They hadn't been implemented, but they were all a very real part of ES. > > > > Whoa. Is that just misinformed or intentionally misleading? They have > never been "part of ES" in any meaningful sense. It was not that they had > not been implemented; it was that they had not even been defined. To say > they are a "real part of ES" is a strange interpretation of the meaning of > the word "real". The notion that we would choose features to work on based > on some designation of certain keywords as reserved long ago, and that they > are now "languishing", is odd. Why not focus on "implementing" enum, or > final, or throws, or any other of the dozens of reserved words? > > > > Having said that, I think it is a valid general principle that as > language designers we should be very reluctant to use magic characters. > `**` is fine, of course, as is `=>`, or even `@` for decorators. > Personally, I don't think the problem of access modifiers rises to the > level of commonality and need for conciseness that would justify eating up > another magic character. We also don't want JS to start looking like Perl > or APL. > > > > Speaking as a self-appointed representative of Plain Old Programmers, I > do feel a need for private fields, although it was probably starting to > program more in TS that got me thinking that way. However, to me it feels > odd to tie this directly to `class` syntax. Why can't I have a private > field in a plain old object like `{a: 1}` (i.e., that would only be > accessible via a method on that object? We already have properties which > are enumerable and writable, for example, independent of the class > mechanism. Why not have properties which are private in the same way? > > > > The problem,of course, is that even assuming the engines implemented the > `private` property on descriptors, I obviously don't want to have to write > `Object.create({}, {a: {value: 22, private: true})`. So the problem can be > restated as trying to find some nice sugar for writing the above. You > know, something like `{a<private>: 22}`. That's obviously a completely > random syntax suggestion, just to show the idea. Perhaps we'd prefer to > have the access modifiers be specifiable under program control as an object > itself, to allow something like > > > > ``` > > const PRIVATE = {private: true}; > > > > const myObject = {a(<PRIVATE>: 2; } > > ``` > > > > But what would the precise meaning of such as `private` descriptor > property be? In operational terms, it could suffice to imagine (as a > behavior, not as an implementation strategy) that objects would have a flag > that would skip over private properties when doing property lookups. I > think the right implementation is to have a private property look like it's > not there at all when access is attempted from outside the object (in other > words, is undefined), rather than some kind of `PrivatePropertyAccessError`. > > > > The above approach ought to be extensible to class notation: > > > > ``` > > class Foo ( > > bar<PRIVATE>(): { return 22; } > > } > > ``` > > > > which would end up being something like > `Object.defineProperty(Foo.prototype, "bar", {value() {return 22; }, > private: true})`. > > > > Or when classes get instance variables: > > > > ``` > > class Foo { > > bar<PRIVATE> = 22; > > ``` > > > > Was anything along these lines already brought up in this discussion? > > Yes. There are a couple answers: > > - If you have the <PRIVATE> marker on both definitions and accesses of the > property, then you get a proposal that's essentially isomorphic to the > committee's current private proposal. > > - If you have the <PRIVATE> marker on definitions but not accesses of the > property, then the proposal leaks private state like a sieve: > > For example: > > class Foo ( > x<PRIVATE>; > > SetX() {this.x = <some secret>;} > } > > To discover what's written into the supposedly private x, just call SetX > on an object that's not an instance of Foo. > > There are analogous examples for reading instead of writing. > > Waldemar > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180808/b4272655/attachment-0001.html>
The POC has been completed. This is a completely viable POC in that it can
be used in its present form in production code. I cannot speak for its
performance yet as I have done no performance testing on it. Seeing as how
it uses Proxy extensively to control access to private declarations, it is
likely to be slow. So this POC may not be suitable for use in high
performance applications. The biggest pain point for this POC is the fact
that Function.caller
and arguments.callee
have been completely crippled
to the the point of being useless. While I understand that their presence
prevented optimizations, it would be far more useful to allow the existence
of something like Function.callerFrame
object with a containsFn(fn)
method to allow exact method identification without call access, and a
previousFrame()
method to allow walking the call stack... but that's an
issue for a different proposal.
The POC has been completed. This is a completely viable POC in that it can be used in its present form in production code. I cannot speak for its performance yet as I have done no performance testing on it. Seeing as how it uses Proxy extensively to control access to private declarations, it is likely to be slow. So this POC may not be suitable for use in high performance applications. The biggest pain point for this POC is the fact that `Function.caller` and `arguments.callee` have been completely crippled to the the point of being useless. While I understand that their presence prevented optimizations, it would be far more useful to allow the existence of something like `Function.callerFrame` object with a `containsFn(fn)` method to allow exact method identification without call access, and a `previousFrame()` method to allow walking the call stack... but that's an issue for a different proposal. On Sun, Aug 5, 2018 at 9:41 AM Ranando King <kingmph at gmail.com> wrote: > Just released the POC code for review. Using the `Privacy()` export, you > can now wrap any class or object definition and add `private` and > `protected` members. To add privileged members to a `class`, declare a > function `static [Privacy.Data]() {}` and return an object containing your > `public` data and `private`/`protected` members. To add privileged members > to any object, declare the field name with array notation, including the > privilege level in the name. The "internal" privilege level has been left > out of this POC and will be released as a separate proposal if this > proposal gains any traction. > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180815/ef5e9397/attachment.html>
I've written up a new draft proposal based on my own work with ES5 & ES6 compatible classes with fields. That can be found here. I'm already aware of the class-members proposal, but I think it breaks far to many things and doesn't do anything to maintain the notion that "
class
is just syntactic sugar".This proposal is specifically based on the code here. I've also got a repl.it that shows the same code running.
The idea behind the proposal is that instead of injecting a lot of new logic into how
class
works, let's allowclass
to remain syntactic sugar, and put that extra ability into object declarations instead. Then simply allowclass
to do the same with it's own prototypes.