Re:!Re: proposal: Object Members

classic Classic list List threaded Threaded
61 messages Options
1234
Reply | Threaded
Open this post in threaded view
|

Re:!Re: proposal: Object Members

Darien Valentine
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 :)


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

Re: !Re: proposal: Object Members

Darien Valentine
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
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: !Re: proposal: Object Members

Ranando King
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 <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

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

Re: !Re: proposal: Object Members

Darien Valentine
> 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:


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 <[hidden email]> 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 <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

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

Re: !Re: proposal: Object Members

Ranando King
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 <[hidden email]> 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:


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 <[hidden email]> 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 <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

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

Re: !Re: proposal: Object Members

Darien Valentine
> 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 <[hidden email]> 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 <[hidden email]> 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:


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 <[hidden email]> 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 <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

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

Re: !Re: proposal: Object Members

Isiah Meadows-2
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
[hidden email]
www.isiahmeadows.com


On Sun, Jul 29, 2018 at 12:23 AM, Darien Valentine
<[hidden email]> 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 <[hidden email]> 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 <[hidden email]>
>> 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 <[hidden email]> 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
>>>> <[hidden email]> 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
>>>>> [hidden email]
>>>>> https://mail.mozilla.org/listinfo/es-discuss
>
>
> _______________________________________________
> es-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es-discuss
>
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: !Re: proposal: Object Members

Ranando King
In reply to this post by Darien Valentine
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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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:


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 <[hidden email]> 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 <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

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

Re: !Re: proposal: Object Members

Darien Valentine
In reply to this post by Isiah Meadows-2
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 <[hidden email]> 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
[hidden email]
www.isiahmeadows.com


On Sun, Jul 29, 2018 at 12:23 AM, Darien Valentine
<[hidden email]> 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 <[hidden email]> 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 <[hidden email]>
>> 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 <[hidden email]> 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
>>>> <[hidden email]> 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
>>>>> [hidden email]
>>>>> https://mail.mozilla.org/listinfo/es-discuss
>
>
> _______________________________________________
> es-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es-discuss
>

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

Re: !Re: proposal: Object Members

Isiah Meadows-2
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
[hidden email]
www.isiahmeadows.com


On Sun, Jul 29, 2018 at 8:00 PM, Darien Valentine <[hidden email]> 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 <[hidden email]>
> 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
>> [hidden email]
>> www.isiahmeadows.com
>>
>>
>> On Sun, Jul 29, 2018 at 12:23 AM, Darien Valentine
>> <[hidden email]> 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 <[hidden email]> 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
>> >> <[hidden email]>
>> >> 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 <[hidden email]>
>> >>> 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
>> >>>> <[hidden email]> 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
>> >>>>> [hidden email]
>> >>>>> https://mail.mozilla.org/listinfo/es-discuss
>> >
>> >
>> > _______________________________________________
>> > es-discuss mailing list
>> > [hidden email]
>> > https://mail.mozilla.org/listinfo/es-discuss
>> >
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: !Re: proposal: Object Members

Michael Theriot
In reply to this post by Isiah Meadows-2
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 <[hidden email]> 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
[hidden email]
www.isiahmeadows.com


On Sun, Jul 29, 2018 at 12:23 AM, Darien Valentine
<[hidden email]> 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 <[hidden email]> 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 <[hidden email]>
>> 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 <[hidden email]> 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
>>>> <[hidden email]> 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
>>>>> [hidden email]
>>>>> https://mail.mozilla.org/listinfo/es-discuss
>
>
> _______________________________________________
> es-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es-discuss
>
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

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

Re: !Re: proposal: Object Members

Isiah Meadows-2
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
[hidden email]
www.isiahmeadows.com


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

Re: !Re: proposal: Object Members

Michael Theriot
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 <[hidden email]> 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
[hidden email]
www.isiahmeadows.com


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

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

Re: !Re: proposal: Object Members

Isiah Meadows-2
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
[hidden email]
www.isiahmeadows.com


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

Re: !Re: proposal: Object Members

Michael Theriot
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 <[hidden email]> 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
[hidden email]
www.isiahmeadows.com


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

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

Re: !Re: proposal: Object Members

Isiah Meadows-2
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
[hidden email]
www.isiahmeadows.com


On Mon, Jul 30, 2018 at 1:25 AM, Michael Theriot
<[hidden email]> 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 <[hidden email]> 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
>> [hidden email]
>> www.isiahmeadows.com
>>
>>
>> On Sun, Jul 29, 2018 at 10:55 PM, Michael Theriot
>> <[hidden email]> 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 <[hidden email]> 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
>> >> [hidden email]
>> >> www.isiahmeadows.com
>> >>
>> >>
>> >> On Sun, Jul 29, 2018 at 10:05 PM, Michael Theriot
>> >> <[hidden email]> 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 <[hidden email]>
>> >> > 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
>> >> >> [hidden email]
>> >> >> www.isiahmeadows.com
>> >> >>
>> >> >>
>> >> >> On Sun, Jul 29, 2018 at 12:23 AM, Darien Valentine
>> >> >> <[hidden email]> 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 <[hidden email]>
>> >> >> > 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
>> >> >> >> <[hidden email]>
>> >> >> >> 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 <[hidden email]>
>> >> >> >>> 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
>> >> >> >>>> <[hidden email]> 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
>> >> >> >>>>> [hidden email]
>> >> >> >>>>> https://mail.mozilla.org/listinfo/es-discuss
>> >> >> >
>> >> >> >
>> >> >> > _______________________________________________
>> >> >> > es-discuss mailing list
>> >> >> > [hidden email]
>> >> >> > https://mail.mozilla.org/listinfo/es-discuss
>> >> >> >
>> >> >> _______________________________________________
>> >> >> es-discuss mailing list
>> >> >> [hidden email]
>> >> >> https://mail.mozilla.org/listinfo/es-discuss
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: !Re: proposal: Object Members

Michael Theriot
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 <[hidden email]> 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
[hidden email]
www.isiahmeadows.com


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

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

Re: !Re: proposal: Object Members

Ranando King
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 <[hidden email]> 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 <[hidden email]> 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
[hidden email]
www.isiahmeadows.com


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

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

Re: !Re: proposal: Object Members

Waldemar Horwat
In reply to this post by Isiah Meadows-2
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
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: !Re: proposal: Object Members

Isiah Meadows-2
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
[hidden email]
www.isiahmeadows.com


On Mon, Jul 30, 2018 at 6:35 PM, Waldemar Horwat <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
1234