Some questions about Private Name Objects
Le 27/08/2012 15:34, Matthew Robb a écrit :
I'm trying to determine whether when compiling into ES3/5 you could get away with not attaching some .privates property to the object and instead just put a closure around the definitions.
For that question, the answer is you cannot. You cannot faithfully polyfill private names. Interesting experiment by Brandon Benvie : bbenvie.com/articles/2012-06-06/ES6-Private-Names-Shim
One of these things we need the platform to do for us.
A name object is not much different from using a string: let myname1 = "S4u-1_tlzUI"; let myname2 = new Name();
someobj[myname1] = 123; someobj[myname2] = 123;
// In a constructor or method: this[myname1] = "abc"; this[myname2] = "abc";
You can pass both around and create properties on any (non-frozen and extensible) object. The main difference is that if you use myname2 then the property won’t be detected via the usual means (Object.keys, Object.getOwnPropertyNames, in operator, for-in loop, etc.). You need the value in myname2 to access properties created via it. But you can let other parties know about that value. That’s what makes it impossible to use closures to simulate this feature.
SO it has to be constructed via new Name() or will it automatically create Name objects when it encounters an assignment of that form? If you do have to create it does that mean in order to access it at all you would need to be in scope of myname2?
My question I think boils down to whether access is SCOPE gated or OBJECT gated:
var myClass = (function(){ class myClass { constructor(){ this[test] = 0; } }
return myClass; })()
myClass.prototype.getTest = function() { return this[test] }
Is the above perfectly valid?
Le 27/08/2012 16:55, Matthew Robb a écrit :
SO it has to be constructed via new Name() or will it automatically create Name objects when it encounters an assignment of that form? If you do have to create it does that mean in order to access it at all you would need to be in scope of myname2?
My question I think boils down to whether access is SCOPE gated or OBJECT gated:
var myClass = (function(){ class myClass { constructor(){ this[test] = 0; } }
return myClass; })()
myClass.prototype.getTest = function() { return this[test] }
Is the above perfectly valid?
This cannot work, because your inherited method needs an access to the private name in your variable 'test' (which in your example is neither declared nor initialized).
To rewrite your example:
var myClass = (function(){ var test = new Name();
class myClass {
constructor(){
this[test] = 0;
}
getTest: function(){return this[test]};
}
return myClass;
})();
In this rewritten version, test is being declared in an encapsulating function scope, getTest will naturally gets in myClass.prototype (by definition of what the class syntax desugars to IIRC) and your inherited method will have access to your private name.
On Aug 27, 2012, at 16:55 , Matthew Robb <matthewwrobb at gmail.com> wrote:
SO it has to be constructed via new Name() or will it automatically create Name objects when it encounters an assignment of that form? If you do have to create it does that mean in order to access it at all you would need to be in scope of myname2?
My question I think boils down to whether access is SCOPE gated or OBJECT gated:
var myClass = (function(){ class myClass { constructor(){ this[test] = 0; } }
return myClass; })()
myClass.prototype.getTest = function() { return this[test] }
Is the above perfectly valid?
No, ES.next would complain about the undeclared variable test
(variable scope, not object). The closest you can get to simulating is by using strings with UUIDs. That avoids the name clashes, but doesn’t hide the properties.
AH okay thanks guys, questions answered!
So why could this not desugar to?:
var myClass = (function(){ var __test;
function myClass() {
__test = 0;
}
myClass.prototype.getTest = function(){ return __test; }
return myClass;
})();
Sorry, I mislead you. Let me retry:
class myClass{
private test;
constructor(){
this[test] = 0;
}
getTest(){return this[test]};
}
desugars to:
var myClass = (function(){
var test = new Name();
function myClass(){
this[test] = 0;
}
myClass.prototype.getTest = function(){return this[test]};
return myClass;
})();
I'm not up-to-date on exact syntax, but that's more or less what you'd get. An interesting point you're bringing is that the desugared version could just use variables in method scopes instead of private names. It could yield in this alternative desugaring:
var myClass = (function(){
var test;
function myClass(){
test = 0;
}
myClass.prototype.getTest = function(){return test};
return myClass;
})();
It can be read in the code why it doesn't work. There is a unique 'test' variable for all instances and not a test variable for each, this makes the "getTest" method pointless in this example (because it gets the last set value and not the value of the current instance). Being able to attach private data to individual objects and being able to access this data in inherited method is one of the use case that require private names.
I hope I have make things more clear.
I know this has some serious ugly to it but in the spirit of knowing what COULD potentially create the expected behavior, would this not do the trick?
var myClass = (function(){
function myClass(){
var __priv = Object.create(this);
var __this = __priv.__this = this;
this.getTest = function(){
return myClass.prototype.getTest.apply((this
===__this)?__priv:this, arguments); }
__priv.test = 0;
}
myClass.prototype.getTest = function() {
var __this = this.__this || this, __private = this;
return __private.test;
}
return myClass;
})();
Le 27/08/2012 19:54, Matthew Robb a écrit :
I know this has some serious ugly to it but in the spirit of knowing what COULD potentially create the expected behavior, would this not do the trick?
var myClass = (function(){
function myClass(){ var __priv = Object.create(this); var __this = __priv.__this = this; this.getTest = function(){
If you're willing to have functions as own properties in each instance, you need the method on the prototype any longer. If JS engines do their job properly, they will store the function body once and just create new function objects for each instance (which you do anyway, so it has no extra cost)
return myClass.prototype.getTest.apply((this
===__this)?__priv:this, arguments); }
__priv.test = 0; } myClass.prototype.getTest = function() { var __this = this.__this || this, __private = this; return __private.test; } return myClass; })();
Version with having a function on each instance:
function myClass(){
var test = 0;
this.getTest = function(){
return test;
};
}
No underscore ;-)
Through the different messages we've shared, you've covered the different ways to do encapsulation and code sharing and the conclusion is that either you need to add a function to each object (which yield a linear cost for no reason) or your abstaction leaks if you want to prototype function to access per-object data. A last alternative is to associate private data via a WeakMap (it can be shimmed in ES5 with the same garbage collection properties and with good performances) that inherited functions all have access to. It works, but it's burdensome and doesn't read as well as object properties.
A last alternative is to associate private data via a WeakMap (it can be shimmed in ES5 with the same garbage collection properties and with good performances) that inherited functions all have access to. It works, but it's burdensome and doesn't read as well as object properties.
On the other hand, private names seem to add complexity to proxies and mixin composition.
In the end I am not looking for reads well, I am looking for minimal semantic difference that can ideally work as a target for compiling to es3. I am PREFERRING to not have functions as ownProperties. I can't use hard binding because if the function is assigned to some place else it should no longer be given access to the private name's it would have had as an instance method/property (I'm assuming this actually).
I have done a little more playing around and am currently right about here:
class myClass { private test; constructor() { this[test] = 0; } getTest() this[test]; doNothing(){} }
COMPILES TO:
var myClass = (function(){
var __myClass = function __myClass(__privates, __this) {
// All class methods using private names would be defined here
this.getTest = function() {
// All appearances of this[test] would be replaced by the below
ternary operation return (this === __this ? __privates : this).test; }
return (__this && (__this.__proto__ = this)) ? __privates : this;
}
var myClass = __myClass.prototype.constructor = function myClass() {
var __privates = new __myClass({}, this);
// Constructor body
__privates.test = 0;
}
myClass.prototype = __myClass.prototype = new __myClass()
myClass.prototype.doNothing = function(){}
return myClass;
})();
Le 27/08/2012 21:22, Kevin Smith a écrit :
A last alternative is to associate private data via a WeakMap (it can be shimmed in ES5 with the same garbage collection properties and with good performances) that inherited functions all have access to. It works, but it's burdensome and doesn't read as well as object properties.
On the other hand, private names seem to add complexity to proxies
A huge share of this complexity has been removed in recent discussions [1]. Conclusions seem to have reach consensus on es-discuss, but nothing has been officially accepted by TC39
and mixin composition.
What do you mean? Private name allow to do mixins without collision. It seems to reduce the complexity than increasing it in my opinion.
David
[1] See discussion starting at esdiscuss/2012-July/024212
On Monday, August 27, 2012 at 3:34 PM, Matthew Robb wrote:
In the end I am not looking for reads well, I am looking for minimal semantic difference that can ideally work as a target for compiling to es3. I am PREFERRING to not have functions as ownProperties. I can't use hard binding because if the function is assigned to some place else it should no longer be given access to the private name's it would have had as an instance method/property (I'm assuming this actually).
I have done a little more playing around and am currently right about here:
class myClass { private test;
Just a heads up: max-min classes do not include support for declaring data properties or private variables in the class body.
Apologies in advance for the length...
A huge share of this complexity has been removed in recent discussions [1]. Conclusions seem to have reach consensus on es-discuss, but nothing has been officially accepted by TC39
As I understand it, the solution arrived at was to provide a (possibly updatable) collection of private name objects to the Proxy constructor and let the engine take care of the details. That sounds good, and good work!
What do you mean? Private name allow to do mixins without collision. It
seems to reduce the complexity than increasing it in my opinion.
Unique (non-private) names offer mixins without collisions. But any general mixin utility will fail if the source object relies on privately named methods for its functionality:
// Create a private name
let privateMethodName = new Name();
// Create a "class" that naively defines and uses a privately-named
method function A() {} A.prototype.doSomething = function() { thisprivateMethodName; }; A.prototype[privateMethodName] = function() {};
// Create another "class"
function B() {}
// Attempt to mix A's functionality into B using a library function
mixin(A, B);
// Fails: privateMethodName is undefined
new B().doSomething();
Neither the destination object nor the library function "mixin" has access to the private name, and therefore the privately named method will not get copied to the destination object, and the mixin will fail.
Clearly the solution above would be to use a unique name, instead of a private name. But that begs the question: why do we need private, secured names at all? They complicate the object model without a compelling use case, as far as I can tell. If there are use cases, let's see them.
In any situation where you want to really secure access to data or code, the ergonomics of your code is going to be the least of your worries. Security is just generally difficult, and that "soft-tissue" cost will have to be borne by the developer whether they use WeakMaps or private names or closures or whatever. Why complicate the object model, and in the process invalidate the current maxim that all properties are reflective, when the weight will still fall on the developer anyway?
Just an argument to consider...
Le 28/08/2012 15:38, Kevin Smith a écrit :
Apologies in advance for the length...
Don't. We're here to discuss as the mailing-list name suggests. And if what you need to say is long, so be it.
A huge share of this complexity has been removed in recent discussions [1]. Conclusions seem to have reach consensus on es-discuss, but nothing has been officially accepted by TC39
As I understand it, the solution arrived at was to provide a (possibly updatable) collection of private name objects to the Proxy constructor and let the engine take care of the details. That sounds good, and good work!
Exactly. I'm glad there is one more fan of the proposal :-)
What do you mean? Private name allow to do mixins without collision. It seems to reduce the complexity than increasing it in my opinion.
Unique (non-private) names offer mixins without collisions. But any general mixin utility will fail if the source object relies on privately named methods for its functionality:
(code snippet)
Neither the destination object nor the library function "mixin" has access to the private name, and therefore the privately named method will not get copied to the destination object, and the mixin will fail.
Clearly the solution above would be to use a unique name, instead of a private name.
Indeed.
But that begs the question: why do we need private, secured names at all? They complicate the object model without a compelling use case, as far as I can tell. If there are use cases, let's see them.
Private names enable easy encapsulation & code sharing at the same time (which is awkward at best nowadays). As a result of the current awkwardness, developer sacrifice encapsulation by using publicly accessible fields using the _field convention. I could name dozens of Node.js library doing that. And sometimes, developers use a private field while they shouldn't, but it makes their life easier, and abstraction leak, etc.
It all start because of a fundamental missing piece in the language that is the ability to add a private property to an object. Not socially private as per the _field convention. I mean actually private, enforced by the runtime.
In any situation where you want to really secure access to data or code, the ergonomics of your code is going to be the least of your worries.
I disagree. A good part of the work toward securing an application is reviewing it. The easier the review is, the less likely you are to leave holes behind. The ergonomics of the code is a security feature.
Security is just generally difficult, and that "soft-tissue" cost will have to be borne by the developer whether they use WeakMaps or private names or closures or whatever. Why complicate the object model, and in the process invalidate the current maxim that all properties are reflective, when the weight will still fall on the developer anyway?
I arrived on es-discuss at a time where private names had been decided to be the mecanism that answer all above-listed constraints. It seems to do the job. I have intuitive concerns about how to use them in large scale applications and preserve proper code sharing, but nothing concrete to either validate nor invalidate this intuition. In a nutshell, I'm wondering whether private names will be easy to use in complex cases. I'm not specifically against the idea of complicating the object model if the benefit is developers stopping _fields. I am open anyway to see if there is an idea that's alternative to private name and would solve all above constraints.
There is still time since no one has implemented private names (or symbols as it's their new name which I'm still not used to) yet.
Unique (non-private) names offer mixins without collisions. But any general mixin utility will fail if the source object relies on privately named methods for its functionality: ... Neither the destination object nor the library function "mixin" has access to the private name, and therefore the privately named method will not get copied to the destination object, and the mixin will fail.
Clearly the solution above would be to use a unique name, instead of a private name.
Alternatively, and this has a more OOP feeling to it, one could put the objects in control of iterations, with separate, overridable iterators for separate purposes. One such purpose would be an iterator to be used for an object clone/extend operator. The default clone iterator would skip private properties, but those could be added by overriding.
Such a clone operator would itself need to be private, only callable for property copying (eg, with a built-in extend), but with this caveat, it should be possible to copy private properties without exposing their keys, and with the source object in full control of whether or not private properties may be copied.
Is this line of reasoning too far off, or has it been considered to add such extend/clone iterators?
Claus
2012/8/27 David Bruant <bruant.d at gmail.com>
Le 27/08/2012 21:22, Kevin Smith a écrit :
On the other hand, private names seem to add complexity to proxies
A huge share of this complexity has been removed in recent discussions [1]. Conclusions seem to have reach consensus on es-discuss, but nothing has been officially accepted by TC39
I'm planning to bring it up at the next meeting.
It all start because of a fundamental missing piece in the language that is the ability to add a private property to an object. Not socially private as per the _field convention. I mean actually private, enforced by the runtime.
I've been pondering this for a while now, and I think I've figured out what's bothering me...
I think privately named fields are a great addition to the language:
let privateThing = new Symbol(); // Or whatever the API is...
class X {
constructor() { this[privateThing] = "something"; }
}
This is good.
However (imagining that we have Allen's private name syntax):
let privateThing = new Symbol(); // Or whatever the API is...
class X {
@privateThing() { console.log("I'm private!"); }
}
This is not, because now class X is (probably unintentionally) un-mixable, for the reasons stated previously. As we said before, we could use a unique name here, but the distinction between private and unique names is very subtle and probably easy to miss.
We are conflating the privacy solution with the name-conflict solution. Since each solution has different and subtle runtime implications, there should be a clear and distinct way for the user to select between them.
I argue that the current proposal does not make a sufficient distinction between private and unique names to lead the user to the correct choice.
If that is a problem, then what are the options?
- Is method name-collision a practical problem, or just a theoretical problem? If it's just a theoretical problem, then we don't need unique names, and in teaching the language we can simply guide users away from trying to create "private methods". In fact, without supporting syntax it's unlikely that users would even bother trying to create them in the first place.
(This would mean that the iterator hook would need to be called simply "iterator".)
- Provide two distinct constructors for private and unique names, and provide supporting method syntax ONLY to unique names. This will ensure that private names are not incorrectly used as prototype method names.
(If unique names are provided, then there will have to be some supporting method syntax, as Allen has suggested.)
I currently lean toward option 1.
On Thu, Sep 13, 2012 at 8:37 AM, Kevin Smith <khs4473 at gmail.com> wrote:
- Is method name-collision a practical problem, or just a theoretical problem? If it's just a theoretical problem, then we don't need unique names, and in teaching the language we can simply guide users away from trying to create "private methods". In fact, without supporting syntax it's unlikely that users would even bother trying to create them in the first place.
Yes. This is a real problem.
It is a common problem that we see a lot with "private" members using naming conventions.
class Base { constructor() { this._element = ...; } }
class Derived extends Base { constructor() { this._element = ...; // OOPS! } }
On Thu, Sep 13, 2012 at 12:09 PM, Erik Arvidsson <erik.arvidsson at gmail.com>wrote:
On Thu, Sep 13, 2012 at 8:37 AM, Kevin Smith <khs4473 at gmail.com> wrote:
- Is method name-collision a practical problem, or just a theoretical problem? If it's just a theoretical problem, then we don't need unique names, and in teaching the language we can simply guide users away from trying to create "private methods". In fact, without supporting syntax it's unlikely that users would even bother trying to create them in the first place.
Yes. This is a real problem.
It is a common problem that we see a lot with "private" members using naming conventions.
class Base { constructor() { this._element = ...; } }
class Derived extends Base { constructor() { this._element = ...; // OOPS! } }
Another good example where this is a problem is on prototype chains, a good example of which you parenthetically noted (iterators). With unique names it becomes feasible to hang any properties and methods you want off of prototypes without worrying about collision. For instance, imagine an persistance lib with a Record.prototype.save method:
var rec = new Record({ save: 'whoops' });
rec.save() // TypeError: Property 'save' is not a function
And thus we all fall back to the lovely Record.prototype.save.call(rec) pattern. Unique names neatly sidestep this, giving us back our prototype chains.
Another good example where this is a problem is on prototype chains, a good example of which you parenthetically noted (iterators). With unique names it becomes feasible to hang any properties and methods you want off of prototypes without worrying about collision. For instance, imagine an persistance lib with a Record.prototype.save method:
var rec = new Record({ save: 'whoops' }); rec.save() // TypeError: Property 'save' is not a function
And thus we all fall back to the lovely Record.prototype.save.call(rec) pattern. Unique names neatly sidestep this, giving us back our prototype chains.
Interesting -
On Thu, Sep 13, 2012 at 1:46 PM, Dean Landolt <dean at deanlandolt.com> wrote:
On Thu, Sep 13, 2012 at 12:09 PM, Erik Arvidsson <erik.arvidsson at gmail.com
wrote:
On Thu, Sep 13, 2012 at 8:37 AM, Kevin Smith <khs4473 at gmail.com> wrote:
- Is method name-collision a practical problem, or just a theoretical problem? If it's just a theoretical problem, then we don't need unique names, and in teaching the language we can simply guide users away from trying to create "private methods". In fact, without supporting syntax it's unlikely that users would even bother trying to create them in the first place.
Yes. This is a real problem.
It is a common problem that we see a lot with "private" members using naming conventions.
class Base { constructor() { this._element = ...; } }
class Derived extends Base { constructor() { this._element = ...; // OOPS! } }
Another good example where this is a problem is on prototype chains, a good example of which you parenthetically noted (iterators). With unique names it becomes feasible to hang any properties and methods you want off of prototypes without worrying about collision. For instance, imagine an persistance lib with a Record.prototype.save method:
var rec = new Record({ save: 'whoops' }); rec.save() // TypeError: Property 'save' is not a function
And thus we all fall back to the lovely Record.prototype.save.call(rec) pattern. Unique names neatly sidestep this, giving us back our prototype chains.
This could also be designed to use a WeakMap as the "store" for the data :)
On Thu, Sep 13, 2012 at 2:59 PM, Rick Waldron <waldron.rick at gmail.com>wrote:
On Thu, Sep 13, 2012 at 1:46 PM, Dean Landolt <dean at deanlandolt.com>wrote:
On Thu, Sep 13, 2012 at 12:09 PM, Erik Arvidsson < erik.arvidsson at gmail.com> wrote:
On Thu, Sep 13, 2012 at 8:37 AM, Kevin Smith <khs4473 at gmail.com> wrote:
- Is method name-collision a practical problem, or just a theoretical problem? If it's just a theoretical problem, then we don't need unique names, and in teaching the language we can simply guide users away from trying to create "private methods". In fact, without supporting syntax it's unlikely that users would even bother trying to create them in the first place.
Yes. This is a real problem.
It is a common problem that we see a lot with "private" members using naming conventions.
class Base { constructor() { this._element = ...; } }
class Derived extends Base { constructor() { this._element = ...; // OOPS! } }
Another good example where this is a problem is on prototype chains, a good example of which you parenthetically noted (iterators). With unique names it becomes feasible to hang any properties and methods you want off of prototypes without worrying about collision. For instance, imagine an persistance lib with a Record.prototype.save method:
var rec = new Record({ save: 'whoops' }); rec.save() // TypeError: Property 'save' is not a function
And thus we all fall back to the lovely Record.prototype.save.call(rec) pattern. Unique names neatly sidestep this, giving us back our prototype chains.
This could also be designed to use a WeakMap as the "store" for the data :)
Sure, but now we're back to the whole object.get/set pattern -- to be otherwise rendered irrelevant by the Object.observe proposal (fingers crossed). There's clearly demand for treating objects as records instead of playing interface gymnastics. And when writing generic code we don't always have that luxury. Names (or Symbols, or whatever we're calling them these days) finally allow us to use the prototype chain safely w/ high integrity lookups.
The real point I'm trying to make is that Name objects give us something akin to clojure's protocols. Imagine an "orm" protocol -- this is just a set of names that must exist on an object (or its proto chain). An object can implement any number of protocols (or interfaces, or whatever) without fear of conflict. You can easily override any implementation so long as you have a handle on the appropriate name object. This is easier, better looking and more correct than anything we can do today. It's not too disimilar from using using instanceof as a brand, but without the pitfalls (it doesn't fall flat crossing sandbox boundaries). This is a safe and flexible inheritance model that works just as a js programmer would expect, all without begging for mutable or multiple prototypes.
The real point I'm trying to make is that Name objects give us something akin to clojure's protocols. Imagine an "orm" protocol -- this is just a set of names that must exist on an object (or its proto chain). An object can implement any number of protocols (or interfaces, or whatever) without fear of conflict. You can easily override any implementation so long as you have a handle on the appropriate name object. This is easier, better looking and more correct than anything we can do today. It's not too disimilar from using using instanceof as a brand, but without the pitfalls (it doesn't fall flat crossing sandbox boundaries). This is a safe and flexible inheritance model that works just as a js programmer would expect, all without begging for mutable or multiple prototypes.
I think this is a winning argument. So the problem becomes: how can we implement this in a non-fugly way? As it stands (and as Allen has pointed out), we don't currently have the syntax to make this work, even for "iterator":
import iterator from "sys:iterator"; // Sorry, hate @'s : )
class Derived extends Base {
// Hmmm... No way to put iterator here.
}
// Try here? Fugly...
X.prototype[iterator] = function() {
super.doSomething(); // Oops - super outside of class definition : (
};
The absolute minimum we need is a way to specify computed property names in classes (and what the heck, object literals too):
import iterator from "sys:iterator";
class Derived extends Base {
[iterator]() {
super.doSomething(); // Works like a charm!
}
}
This is IMO the best way forward. It's generally useful beyond the use case presented here and does not use up a valuable free ASCII character. It also doesn't require any new symbolism since we already understand that brackets indicate computed property names. Why were computed property names killed again?
On Fri, Sep 14, 2012 at 12:58 PM, Kevin Smith <khs4473 at gmail.com> wrote:
The real point I'm trying to make is that Name objects give us something
akin to clojure's protocols. Imagine an "orm" protocol -- this is just a set of names that must exist on an object (or its proto chain). An object can implement any number of protocols (or interfaces, or whatever) without fear of conflict. You can easily override any implementation so long as you have a handle on the appropriate name object. This is easier, better looking and more correct than anything we can do today. It's not too disimilar from using using instanceof as a brand, but without the pitfalls (it doesn't fall flat crossing sandbox boundaries). This is a safe and flexible inheritance model that works just as a js programmer would expect, all without begging for mutable or multiple prototypes.
I think this is a winning argument. So the problem becomes: how can we implement this in a non-fugly way? As it stands (and as Allen has pointed out), we don't currently have the syntax to make this work, even for "iterator":
import iterator from "sys:iterator"; // Sorry, hate @'s : ) class Derived extends Base { // Hmmm... No way to put iterator here. } // Try here? Fugly... X.prototype[iterator] = function() { super.doSomething(); // Oops - super outside of class definition : ( };
The absolute minimum we need is a way to specify computed property names in classes (and what the heck, object literals too):
import iterator from "sys:iterator"; class Derived extends Base { [iterator]() { super.doSomething(); // Works like a charm! } }
This is IMO the best way forward. It's generally useful beyond the use case presented here and does not use up a valuable free ASCII character. It also doesn't require any new symbolism since we already understand that brackets indicate computed property names. Why were computed property names killed again?
I can't find any evidence that they were, however I did dig up evidence that they were in fact accepted by everyone at the July 2011 meeting:
search that page for "private-name-alternatives.pdf"
On Thu, Sep 13, 2012 at 5:46 PM, Dean Landolt <dean at deanlandolt.com> wrote:
On Thu, Sep 13, 2012 at 2:59 PM, Rick Waldron <waldron.rick at gmail.com>wrote:
On Thu, Sep 13, 2012 at 1:46 PM, Dean Landolt <dean at deanlandolt.com>wrote:
On Thu, Sep 13, 2012 at 12:09 PM, Erik Arvidsson < erik.arvidsson at gmail.com> wrote:
On Thu, Sep 13, 2012 at 8:37 AM, Kevin Smith <khs4473 at gmail.com> wrote:
- Is method name-collision a practical problem, or just a theoretical problem? If it's just a theoretical problem, then we don't need unique names, and in teaching the language we can simply guide users away from trying to create "private methods". In fact, without supporting syntax it's unlikely that users would even bother trying to create them in the first place.
Yes. This is a real problem.
It is a common problem that we see a lot with "private" members using naming conventions.
class Base { constructor() { this._element = ...; } }
class Derived extends Base { constructor() { this._element = ...; // OOPS! } }
Another good example where this is a problem is on prototype chains, a good example of which you parenthetically noted (iterators). With unique names it becomes feasible to hang any properties and methods you want off of prototypes without worrying about collision. For instance, imagine an persistance lib with a Record.prototype.save method:
var rec = new Record({ save: 'whoops' }); rec.save() // TypeError: Property 'save' is not a function
And thus we all fall back to the lovely Record.prototype.save.call(rec) pattern. Unique names neatly sidestep this, giving us back our prototype chains.
This could also be designed to use a WeakMap as the "store" for the data :)
Sure, but now we're back to the whole object.get/set pattern -- to be otherwise rendered irrelevant by the Object.observe proposal (fingers crossed). There's clearly demand for treating objects as records instead of playing interface gymnastics. And when writing generic code we don't always have that luxury. Names (or Symbols, or whatever we're calling them these days) finally allow us to use the prototype chain safely w/ high integrity lookups.
Of course and I've been a constantly vocal champion of Object.observe. Instead of further abusing the "plain object", I think a better way forward is designing smart "record object" constructors that construct instances that are pre-initialized as observable, eg. rwldrn/fact(even that's at the mercy of property name collision (eg. on, off, emit... etc) and desires some form of "purified prototype chain".)
Hey Dean,
I also really love clojure protocols and in fact tried to propose few extensions for private names to make them little more usable as such:
esdiscuss/2012-June/023657, gist.github.com/2967124
Unfortunately thread did not got any replies from anyone.
That being said I did couple of experiments in these area:
Implemented clojure like protocols as library: jeditoolkit.com/2012/03/21/protocol-based-polymorphism.html#post
But after using it for some time I realised it was not very javascripty, so I have prototyped a more minimalistic version, which I'm using happily since then:
I still really wish we could make private names callable such that:
var method = Name()
method(object, arg1, arg2) => object[method](arg1, arg2)
-- Irakli Gozalishvili Web: www.jeditoolkit.com
I just spoke to Allen and need to make a correction the post above: computed properties were, in fact, later dropped.
So I have been trying to figure out from the wiki precisely how Private Name Objects work. When assigned/defined does it expose that property to all sibling properties/methods or just those present at the time of assign/define? If you add on to the object later to the new properties get access?
var Person = { [hunger]: 100, eat: function(){ --this[hunger]; } }
? Person.getHunger = function() { return this[hunger] }
class Person { constructor(){ this[hunger] = 100; } }
Person.prototype.getHunger = function(){ return this[hunger]; }
Does it make a difference wether it is a 'method' vs a property with a function value?
var Person = { [hunger]: 100, eat: function(){ --this[hunger]; } isHungry: function() { return this[hunger] > 0; } getHunger() { return this[hunger]; } }
I'm trying to determine whether when compiling into ES3/5 you could get away with not attaching some .privates property to the object and instead just put a closure around the definitions.
Thanks ahead of time!
Matthew Robb