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
_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss