using Private name objects for declarative property definition.

Allen Wirfs-Brock allen at wirfs-brock.com
Fri Jul 8 18:21:01 PDT 2011


On Jul 8, 2011, at 2:58 PM, Brendan Eich wrote:

> 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.

An block lambdas would presumably also provide a solution if they were added to the language.
> 
> 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.

There is more general issue about reflection on private members (and I intentionally said "members" here rather than "names").  Some of us believe that private members should never be exposed via reflection.  Others of us think there are plenty of reflection use cases where it is either useful or necessary to reflect using private names.  The current private name objects allows whether or not reflection is permitted using that name  to be set when the private name is created.  The pure declarative form in the proposed class declarations don't have any way to specify that regardless of whether or not private names are used as the underlyng implementation for private members).  If it did allow such control (and private names were used in the implementation), there would still need to be a way to access the private name associated by the class declaration with a specific private member in order to reflect upon it.  Sure you could use getOwnPropertyNames to access all of the property names but if that was all you had how could you be sure of the correspondence between private names and declared private members.

> 

> 
> 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.

The classes proposal states this requirement as "The ability to have private mutable state on publicly frozen objects."  It doesn't say, "add new private properties to publicly frozen objects".  The stated requirement could be meet by doing a preventExtensions on the object and setting each public property's attributes to non-confiburable and non-writable (for data properties) while leaving private properties writable (or even configurable). 

I wouldn't particularly agree with making the other formulation a requirement or with requiring that "soft field" associated with a non-extensible object must be accessible in a referential transparent manner. (In other words, I think explicit look-aside tables are fine for this use case, which in practice I think occurs quite rarely).

> 
> 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?

But if
const Point = {
   @x: 0,
   @y, 0,
      ...
}

means that the private names bound to x and y are instant private, then how would you make x and y friendly between Point and ThreePoint?

const x=Name.create(), y=Name.create();
const Point = {
   @x: 0,
   @y, 0,
      ...
}
const ThreePoint = {
   @x: 0,
   @y: 0,
   @z: 0,
      ...
}

I don't think you want something like this to be controlled by the existence/non-existane of a declaration of the same name in an outer scope.

I think what I would like would be for
  private x;
to be equivalent to
  const x=Name.create();
and to allow that form of private declaration to appear inside an object initializer.

Then you could say:
   private x,y;
   const Point = {
      @x: 0,
      @y, 0,
         ...
   }

or
   const Point = {
      private x,  //another lookahead issue for block lambdas
      private y,  //sure would be nice for it to be private x,y, but...
      @x: 0,
      @y, 0,
         ...
   }

depending on what accessibility you want for the restricted members.

I think that ability to flexibly define the accessibility of member is one of the key features of the private name approach, but it does add a litter verbosity to the most common use case ("class" private)


> 
> 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?

yes, that is my proposal.  However, I'm not sure that it really helps at either the implementation or readability level.  However, it seems like a restriction we could startup with latter relax if there was a good reason to.

> 
> 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.

Yes, I don't think we want to go there so the restriction is mostly token.


> 
> 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.

I assume that both the declaration and reference of private members part of the classes proposal will have to be adjusted to accommodate a private names based approach. 

> 
> 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.
absolutely
> 
> 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/1feecb01/attachment-0001.html>


More information about the es-discuss mailing list