using Private name objects for declarative property definition.

Brendan Eich brendan at mozilla.com
Fri Jul 8 14:58:08 PDT 2011


On Jul 8, 2011, at 12:16 PM, Allen Wirfs-Brock wrote:

> The current Harmony classes proposal http://wiki.ecmascript.org/doku.php?id=harmony:classes includes the concept of private instance members and syntax for defining them.  While it presents a syntax for accessing them (eg, private(foo).bar accesses the private 'bar' member of the object that is the value of foo) there does not yet appear to be consensus acceptance of this access syntax.

Oh, quite the opposite -- everyone on TC39 with whom I've spoken agrees that private(this) syntax is straw that must be burned up. We need new syntax, or else we need to do at least what Dave proposed in "minimal classes": defer private syntax, let programmers use private name objects and explicit [] indexing.

I do think we can say a few more things about private in class that may not be in the requirements on the wiki:

On the level of rationale for why class- and not instance-private, Juan Ignacio Dopazo in private correspondence made a good observation, shown by his example:

class MyClass {
  private foo() {}
  bar() {
    var self = this;
    setTimeout(function () {
      self.foo();
    }, 0);
  }
} 

Because |this| is not lexical, instance- rather than class-private access that requires "this." to the left of the private variable reference does not work unless you use the closure pattern explicitly and abjure |this|.

The straw "private(foo)" syntax doesn't help if the goal is instance privacy, since the inner function has no idea (runtime, never mind compile-time) how to enforce to which particular instance self must refer.

Requiring .bind(this) after the function expression passed to setTimeout can be used to work around such a hypothetical, mandatory "this."-prefix instance-private restriction, but that's onerous and it can be too costly.

Another observation about any class-private scheme we might consider in the current context: private instance variables in many ways (notably not for Object.freeze) act like properties, and the syntax mooted so far casts them in that light. Even if the ES5 reflective APIs, such as Object.getOwnPropertyNames, rightly skip privates on a class instance, proxies may raise the question: how does a private variable name reflect as a property name?

  class Point {
    constructor(x, y) { private x = x, y = y; }
    equals(other) {
     return private(this).x is private(other).x &&
            private(this).y is private(other).y;
    }
    ...
  }

We cannot know how to ask for private-x from other without special syntax of some kind, either at the access point or as a binding declaration affecting all .x (and x: in object literals). So here I use the proposal's straw private(foo) syntax.

Could other be a proxy that somehow has a private data record? Could other denote a class instance whose [[Prototype]] is a proxy? I claim we do not want private(foo) by itself, no .x after, to reify as an object, but if it did, then it seems to me it could be used with a proxy to reflect on private variable names.

There are certainly several choices here, but the one I currently favor as simplest, which creates no new, ad-hoc concepts in the language, is that class-private instance variables are properties named by private name objects.

Per the requirements for private in the classes proposal, this means Object.freeze does not freeze private-named properties, or at least does not make private-named data properties non-writable.

Perhaps Object.preventExtensions should not restrict private-named properties from being added to the object. This seems strange and wrong at first, but if freeze does not affect private-named properties, I'm not sure there is a "defensive consistency" threat from attackers decorating frozen objects with ad-hoc properties named by private name objects that only the attacker could create or access, which cannot collide with the class's private names.

Perhaps this is uncontroversial. I hope so, but I don't assume it is, and I suspect people who worked on the classes proposal may disagree. Cc'ing Mark in particular, since I'm just writing down some preliminary thoughts and intermediate conclusions here, and I could be way off base.

Ok, back to your good point about wanting private names to be usable in object initialisers:


> Each of these approaches seem plausible.  For a side-by-side comparison of the above example using the alternatives see http://wiki.ecmascript.org/lib/exe/fetch.php?id=harmony%3Aprivate_name_objects&cache=cache&media=harmony:private-name-alternatives.pdf .  I'm interested in feedback on the alternatives.

The first thing I'd say is that, whatever the syntax (private prefix, [] around property name, or @ prefix), it seems too verbose to require boilerplate of const __x = Name.create(), etc. before the object literal.

Wouldn't it be better for the prefix, brackets or sigil to by itself define a private name and use it, also putting it in scope for the rest of the initialiser? With temporal dead zone error semantics for use before def, as with const?

On the syntax menu, the [] bracketing seems to allow arbitrary computed property names, but if I'm reading you right, you don't propose that: rather you want "constant" but not static property names, so that the initializer's shape does not depend on evaluating arbitrary expressions to compute property names (whether string-equated public names, or private name objecs). Do I have this right?

If so, I agree we want the syntax to should "here is a private property name!" In this light, private works but is verbose, @ works as well and is concise (maybe too concise for some folks).

I also contend that the static (compile-time, early-error time) shape of object literals in JS, in contrast to the mandatory name-quoting and full evaluation of unquoted name expressions in other languages such as Python, is something to consider keeping.

If so, then const is not enough. We would really need a static evaluation stage where private names could be created and used, but no other expressions evaluated. But such a stage has been rejected in the Harmony era, e.g. for guards.

Modules add a different kind of staging that is not problematic, but two stages for evaluating any given expression or statement is a bridge too far. I'm not in favor of adding another such evaluation regime.


> I've only used object literal in these examples, however the same alternatives should be equally applicable to class declarations.

With classes as proposed, though, the way private name objects would be created would be via "private x = x, y = y;" declarations within the constructor body, as in my Point example above. That seems unfortunate since the scope of these names, whatever the access syntax, is wider than the constructor body. This is one of the open issues with classes I've mentioned here but not quite captured on the wiki page.

But whatever the class syntax, and the disposition of private in class and even classes in ES.next, I agree we should expect private declarative and expression forms to work the same in object initialisers and in classes.

It would be good to get everyone buying into this private-means-property-with-private-name-key-everywhere agreement.

/be
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20110708/ac74b717/attachment.html>


More information about the es-discuss mailing list