Public/private namespaces in harmony classes proposal
On Thu, Jul 7, 2011 at 9:41 PM, Gavin Barraclough <barraclough at apple.com>wrote:
Hi Mark,
In the harmony classes proposal, harmony:classes , I'm interested in understanding the following issue:
One or two namespaces for public properties and private instance
variables [RESOLVED two, Mark’s argument]
Do you remember if this argument was made in email, and if so would anyone happen to know where to look to find this (I've tried a little googling to no avail!), I'd be interested in understanding the rationale behind this decision.
I don't think it was made in before in email. Here goes:
For non-const classes, their instances are extensible by default. Even if you disagree with this default, I think we generally agree that there should at least be an option to make extensible instances.
Say public and private share one namespace. Say extensible instance X has private instance property 'foo'. Say a client of X tries to extend it with a public 'foo' property. What happens?
On Jul 7, 2011, at 9:49 PM, Mark S. Miller wrote:
On Thu, Jul 7, 2011 at 9:41 PM, Gavin Barraclough <barraclough at apple.com> wrote: Hi Mark,
In the harmony classes proposal, harmony:classes , I'm interested in understanding the following issue:
One or two namespaces for public properties and private instance variables [RESOLVED two, Mark’s argument]
Do you remember if this argument was made in email, and if so would anyone happen to know where to look to find this (I've tried a little googling to no avail!), I'd be interested in understanding the rationale behind this decision.
I don't think it was made in before in email. Here goes:
For non-const classes, their instances are extensible by default. Even if you disagree with this default, I think we generally agree that there should at least be an option to make extensible instances.
Say public and private share one namespace. Say extensible instance X has private instance property 'foo'. Say a client of X tries to extend it with a public 'foo' property. What happens?
Ah, I see. It's a fair point, but isn't this already a hazard that the language faces?
Suppose I have two objects, extensible instance X with private instance property 'foo', and extensible object Y upon which I have defined a property 'foo' using Object.defineProperty, setting writable=false. If a client of Y tries to extend if with a public 'foo' property, then this would fail (throwing a TypeError in strict mode code). Would it not be acceptable for the attempted assignment to the private property of X to fail in a similar fashion? It seems that it is already the case that if a client wishes to associate data with a given object under any arbitrary name, then the only truly safe way to do so is through an external mapping such as a weak map / ephemeron table?
Might it be reasonable to make private properties be regular properties on the object, with a new 'private' attribute, similar to the existing writable/configurable attributes? From the perspective of code outside of the associated class, an instance's private property would be non-readable, non-writable and non-configurable (likely also non-enumerable?), with any attempt to get, set, or delete the property failing in a similar manner to an existing writable/configurable attribute violation. It would be great to hear your thoughts on this.
On Jul 7, 2011, at 11:07 PM, Gavin Barraclough wrote:
Ah, I see. It's a fair point, but isn't this already a hazard that the language faces?
Not with private names.
Suppose I have two objects, extensible instance X with private instance property 'foo', and extensible object Y upon which I have defined a property 'foo' using Object.defineProperty, setting writable=false. If a client of Y tries to extend if with a public 'foo' property, then this would fail (throwing a TypeError in strict mode code). Would it not be acceptable for the attempted assignment to the private property of X to fail in a similar fashion?
No, that leaks the fact that there's a private-named object (if you also enumerate public names and do not find such a name; or enumerate property descriptors, etc.).
It seems that it is already the case that if a client wishes to associate data with a given object under any arbitrary name, then the only truly safe way to do so is through an external mapping such as a weak map / ephemeron table?
No, private names are in ES.next:
Might it be reasonable to make private properties be regular properties on the object, with a new 'private' attribute, similar to the existing writable/configurable attributes? From the perspective of code outside of the associated class, an instance's private property would be non-readable, non-writable and non-configurable (likely also non-enumerable?), with any attempt to get, set, or delete the property failing in a similar manner to an existing writable/configurable attribute violation. It would be great to hear your thoughts on this.
See above -- private means you can't probe, let alone collide, for the private name from outside of the abstraction. Private name objects prove much more than a single-bit 'private' attribute -- they allow private, protected, friend, shared-secret, and public-but-guarnateed-unique names.
On Jul 7, 2011, at 11:15 PM, Brendan Eich wrote:
Suppose I have two objects, extensible instance X with private instance property 'foo', and extensible object Y upon which I have defined a property 'foo' using Object.defineProperty, setting writable=false. If a client of Y tries to extend if with a public 'foo' property, then this would fail (throwing a TypeError in strict mode code). Would it not be acceptable for the attempted assignment to the private property of X to fail in a similar fashion?
No, that leaks the fact that there's a private-named object
er, "private property on the object".
On Fri, Jul 8, 2011 at 3:15 AM, Brendan Eich <brendan at mozilla.com> wrote:
On Jul 7, 2011, at 11:07 PM, Gavin Barraclough wrote:
Ah, I see. It's a fair point, but isn't this already a hazard that the language faces?
Not with private names.
There's also something that was probably discussed but never got to this
list:
Private instance properties were considered to be accessible by all instances of the class. That's a new concept in ES. Were there arguments for keeping instance private properties private to the instance?
Juan
On Fri, Jul 8, 2011 at 6:36 AM, Juan Ignacio Dopazo <dopazo.juan at gmail.com>wrote:
On Fri, Jul 8, 2011 at 3:15 AM, Brendan Eich <brendan at mozilla.com> wrote:
On Jul 7, 2011, at 11:07 PM, Gavin Barraclough wrote:
Ah, I see. It's a fair point, but isn't this already a hazard that the language faces?
Not with private names.
There's also something that was probably discussed but never got to this list:
Private instance properties were considered to be accessible by all instances of the class. That's a new concept in ES. Were there arguments for keeping instance private properties private to the instance?
Yes. That's done quite well by lexical capture of the constructor lexical context by per-instance methods:
class Point {
constructor(x, y) {
public getX() { return x; }
...
}
}
which basically sugars the objects-as-closure pattern. If you don't mind the allocation cost, this is superior in almost all ways to the conventional prototype/this-based pattern.
"private" is needed when combining inherited methods with encapsulation. Since inherited methods may apply to any instance of the class and are not automagically bound to any one instance, their access to encapsulated state cannot be any finer than per-class.
On Fri, Jul 8, 2011 at 11:15 AM, Mark S. Miller <erights at google.com> wrote:
On Fri, Jul 8, 2011 at 6:36 AM, Juan Ignacio Dopazo <dopazo.juan at gmail.com
wrote:
Private instance properties were considered to be accessible by all instances of the class. That's a new concept in ES. Were there arguments for keeping instance private properties private to the instance?
Yes. That's done quite well by lexical capture of the constructor lexical context by per-instance methods:
class Point { constructor(x, y) { public getX() { return x; } ... } }
which basically sugars the objects-as-closure pattern. If you don't mind the allocation cost, this is superior in almost all ways to the conventional prototype/this-based pattern.
"private" is needed when combining inherited methods with encapsulation. Since inherited methods may apply to any instance of the class and are not automagically bound to any one instance, their access to encapsulated state cannot be any finer than per-class.
But when the number of methods that need access to the private property rises, this becomes a mess of a constructor. And it also breaks the use of super, doesn't it?
My first thought was: why not just statically replace this.x, with private x, with this[xPrivateName] and forget about accessing private properties of other objects? That would still leave the problem of closures inside methods, though...
Juan
On Jul 8, 2011, at 6:36 AM, Juan Ignacio Dopazo wrote:
On Fri, Jul 8, 2011 at 3:15 AM, Brendan Eich <brendan at mozilla.com> wrote: On Jul 7, 2011, at 11:07 PM, Gavin Barraclough wrote:
Ah, I see. It's a fair point, but isn't this already a hazard that the language faces?
Not with private names.
There's also something that was probably discussed but never got to this list:
Private instance properties were considered to be accessible by all instances of the class. That's a new concept in ES. Were there arguments for keeping instance private properties private to the instance?
Yes, on this list, going back to the Harmony Oslo July 2008 meeting, with followup at the "Kona" Nov. 2008 meeting.
Mark may have notes from the more recent sub-group of TC39ers who championed classes. My recollection is that we chose class-private for both usability and private-name equivalence, but we still have open issues on how one accesses private foo in both |this| and |other| for a dyadic operator method, e.g.
On Jul 8, 2011, at 7:43 AM, Juan Ignacio Dopazo wrote:
My first thought was: why not just statically replace this.x, with private x, with this[xPrivateName] and forget about accessing private properties of other objects? That would still leave the problem of closures inside methods, though...
If xPrivateName is a private name object, then there's no reason to "forget about accessing private properties of other objects". Both this[xPrivateName] and other[xPrivateName] work as you would expect.
On Fri, Jul 8, 2011 at 12:15 PM, Brendan Eich <brendan at mozilla.com> wrote:
On Jul 8, 2011, at 7:43 AM, Juan Ignacio Dopazo wrote:
My first thought was: why not just statically replace this.x, with private x, with this[xPrivateName] and forget about accessing private properties of other objects? That would still leave the problem of closures inside methods, though...
If xPrivateName is a private name object, then there's no reason to "forget about accessing private properties of other objects". Both this[xPrivateName] and other[xPrivateName] work as you would expect.
/be
You are very much right. What are the open issues with privates in classes then?
Juan
On Jul 8, 2011, at 8:45 AM, Juan Ignacio Dopazo wrote:
On Fri, Jul 8, 2011 at 12:15 PM, Brendan Eich <brendan at mozilla.com> wrote: On Jul 8, 2011, at 7:43 AM, Juan Ignacio Dopazo wrote:
My first thought was: why not just statically replace this.x, with private x, with this[xPrivateName] and forget about accessing private properties of other objects? That would still leave the problem of closures inside methods, though...
If xPrivateName is a private name object, then there's no reason to "forget about accessing private properties of other objects". Both this[xPrivateName] and other[xPrivateName] work as you would expect.
/be You are very much right. What are the open issues with privates in classes then?
The wiki lists some. Here are a few from memory:
-
Syntax to use instead of private(this), private(other). The straw private(foo) syntax is too verbose and it wrongly suggests that a private data record is a distinct object. Are we ready to claim @ as prefix and infix operator (with restriction against LineTerminator on its left)?
-
Semantics of private(other).x -- is that a private name access object[xPrivateName] or something else? Without types we don't know what other could be, and it may be a proxy whose handler's traps can observe (something about) the value of x used as a property name.
-
Private prototype methods and other properties may be useful, especially methods. One can wrap a closure around the class and put private helpers there, of course, but with private syntax in the proposal, why stop short?
If we do support private prototype properties, then what are the semantics? Private name objects, as recently noted, have their .public counterparts passed as name parameters to proxy traps, so something about private prototype properties may be observable:
class Victim prototypes Proxy { private protoMethod() { privat(super).protoMethod(); } }
On Jul 8, 2011, at 9:52 AM, Brendan Eich wrote:
If we do support private prototype properties, then what are the semantics? Private name objects, as recently noted, have their .public counterparts passed as name parameters to proxy traps, so something about private prototype properties may be observable:
class Victim prototypes Proxy { private protoMethod() { privat(super).protoMethod(); } }
s/prototypes/prototype/ of course.
On Fri, Jul 8, 2011 at 1:52 PM, Brendan Eich <brendan at mozilla.com> wrote:
On Jul 8, 2011, at 8:45 AM, Juan Ignacio Dopazo wrote:
You are very much right. What are the open issues with privates in classes then?
The wiki lists some. Here are a few from memory:
- Syntax to use instead of private(this), private(other). The straw private(foo) syntax is too verbose and it wrongly suggests that a private data record is a distinct object. Are we ready to claim @ as prefix and infix operator (with restriction against LineTerminator on its left)?
Why do privates need special syntax? Isn't the point to just use them with this.privateWhatever?
- Private prototype methods and other properties may be useful, especially methods. One can wrap a closure around the class and put private helpers there, of course, but with private syntax in the proposal, why stop short?
+1!
If we do support private prototype properties, then what are the semantics? Private name objects, as recently noted, have their .public counterparts passed as name parameters to proxy traps, so something about private prototype properties may be observable:
class Victim prototypes Proxy { private protoMethod() { privat(super).protoMethod(); } }
Isn't that only an issue of "protected" vs "private"?
Juan
On Jul 8, 2011, at 10:20 AM, Juan Ignacio Dopazo wrote:
On Fri, Jul 8, 2011 at 1:52 PM, Brendan Eich <brendan at mozilla.com> wrote: On Jul 8, 2011, at 8:45 AM, Juan Ignacio Dopazo wrote:
You are very much right. What are the open issues with privates in classes then?
The wiki lists some. Here are a few from memory:
- Syntax to use instead of private(this), private(other). The straw private(foo) syntax is too verbose and it wrongly suggests that a private data record is a distinct object. Are we ready to claim @ as prefix and infix operator (with restriction against LineTerminator on its left)?
Why do privates need special syntax? Isn't the point to just use them with this.privateWhatever?
No, because of the other.x problem:
class Point { constructor(x, y) {...} equals(other) { return this.x is other.x && this.y is 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).
If we do support private prototype properties, then what are the semantics? Private name objects, as recently noted, have their .public counterparts passed as name parameters to proxy traps, so something about private prototype properties may be observable:
class Victim prototypes Proxy { private protoMethod() { privat(super).protoMethod(); } }
Isn't that only an issue of "protected" vs "private"?
Sorry, that may be a bad example. Consider the Point example above, where other is a proxy and we use @ for private access:
class Point { constructor(x, y) {...} equals(other) { return @x is other at x && @y is other at y; } ... }
(The unary prefix form @foo is short for binary this at foo.)
The spec has to say what the proxy denoted by "other" sees as the name parameter when its handler's relevant trap is called.
Might it be reasonable to make private properties be regular properties on the object, with a new 'private' attribute, similar to the existing writable/configurable attributes? From the perspective of code outside of the associated class, an instance's private property would be non-readable, non-writable and non-configurable (likely also non-enumerable?), with any attempt to get, set, or delete the property failing in a similar manner to an existing writable/configurable attribute violation. It would be great to hear your thoughts on this.
See above -- private means you can't probe, let alone collide, for the private name from outside of the abstraction. Private name objects prove much more than a single-bit 'private' attribute -- they allow private, protected, friend, shared-secret, and public-but-guarnateed-unique names.
/be
Got it, cheers gents.
G.
In the harmony classes proposal, harmony:classes , I'm interested in understanding the following issue:
Do you remember if this argument was made in email, and if so would anyone happen to know where to look to find this (I've tried a little googling to no avail!), I'd be interested in understanding the rationale behind this decision.