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 <[email protected]> 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 > [email protected] > www.isiahmeadows.com > > > On Sun, Jul 29, 2018 at 10:05 PM, Michael Theriot > <[email protected]> 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 <[email protected]> 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 > >> [email protected] > >> www.isiahmeadows.com > >> > >> > >> On Sun, Jul 29, 2018 at 12:23 AM, Darien Valentine > >> <[email protected]> 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 <[email protected]> > 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 > >> >> <[email protected]> > >> >> 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 <[email protected]> > >> >>> 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 > >> >>>> <[email protected]> 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 > >> >>>>> [email protected] > >> >>>>> https://mail.mozilla.org/listinfo/es-discuss > >> > > >> > > >> > _______________________________________________ > >> > es-discuss mailing list > >> > [email protected] > >> > https://mail.mozilla.org/listinfo/es-discuss > >> > > >> _______________________________________________ > >> es-discuss mailing list > >> [email protected] > >> https://mail.mozilla.org/listinfo/es-discuss >
_______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

