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

_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to