Darien Valentine (2018-07-29T04:23:50.000Z)
> 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>
valentinium at gmail.com (2018-07-29T04:29:29.401Z)
> 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 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?

```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.
valentinium at gmail.com (2018-07-29T04:28:55.307Z)
> 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 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?

```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.
valentinium at gmail.com (2018-07-29T04:27:11.663Z)
> 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.
valentinium at gmail.com (2018-07-29T04:26:51.226Z)
> 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.