About private names
On Sat, Mar 19, 2011 at 7:41 PM, Andrew Dupont <mozilla at andrewdupont.net> wrote:
Or, to use another example: let's say
MyObj
was defined in a different scope, one in whichclone
was assumed to be a public name. Can I do this...private clone = installCloneLibrary(); var twin = [{a:0}, {b:1}].clone(); var thing = MyObj.clone();
... and have it work the way I intend? In other words, once ES5 fails to find something with the
clone
private name defined onMyObj
, will it try to find the property with the public name ofclone
before it gives up? (If I'm reading this right, it won't.)
You're correct -- this won't do what you probably intended. But the great thing about private names is that this is a problem you can locally fix. For example:
private myClone = installCloneLibrary(); var twin = [{a:0}, {b:1}].myClone(); var thing = MyObj.clone();
or
var cloneProp = installCloneLibrary(); var twin = [{a:0}, {b:1}]cloneProp; var thing = MyObj.clone();
will both do what you expect, without requiring changes to either CloneLibrary or MyObj. -- sam th samth at ccs.neu.edu
On Mar 19, 2011, at 7:02 PM, Sam Tobin-Hochstadt wrote:
You're correct -- this won't do what you probably intended. But the great thing about private names is that this is a problem you can locally fix. For example:
private myClone = installCloneLibrary(); var twin = [{a:0}, {b:1}].myClone(); var thing = MyObj.clone();
Yeah, but at that point one might as well just name the function myClone (replacing "my" with some framework-specific prefix) and eschew private names altogether. In this example, being able to name the method "clone" without fear of naming collisions is the whole point.
or
var cloneProp = installCloneLibrary(); var twin = [{a:0}, {b:1}]cloneProp; var thing = MyObj.clone();
This is more palatable, but I wager it's far harder for end-users to grok.
Anyway, now that I've confirmed my suspicions, I'm hesitant about the private names proposal as described. The fact that declaring a certain name as private affects all property name lookups in that scope (all lookups that use the dot operator or object literal syntax, at least) — well, I'm not sure I like the implications. It would mean a new and surprising distinction between dot notation and bracket notation.
As a maintainer of a framework that does quite a bit of built-in extension, I can't imagine us using private names for this purpose. If we wanted to define {}.clone in this manner, we'd be exchanging one potential collision (one property defined in one place) for another (every property defined in the same lexical scope). If the private names had their own operator (e.g., twin#clone vs. twin.clone), it'd at least be worth considering.
I'm sure private names would be useful for other reasons, but IMO it wouldn't solve the problem of safely extending built-ins.
Even though I wrote it, I don't think the clone example on the Private Names pages is very clear so I'll take another trying at explaining the conflict free extension use cases. Just to make sure that we don't confuse things with any assumptions about the implicit semantics of a clone operation I'm going to use a method named "work" instead of "clone".
.#1 A framework writer needs to add a method to object prototype. They use that method extensive internally within the framework.They assign a private name that they internally reference as 'work' for that method. They also provide a way for client code to access that private name.
.#2 An application developer uses the framework from #1 (let's call it MF). They are generally unaware that MF has added an extension method to Object.prototype: var MF = InstallMF(); var thing = MF.getAThing(); //behind the scene calls MF's work method
.#3 The application developer for their own good reasons decides to add a method named 'work' to Object.prototype. MF internally just keeps working: var MF = InstallMF(); Object.prototype.work = function () { ...}; var thing = MF.getAThing(); //behind the scene calls MF's work method, still works fine var appThing = think.work(); //class the app work method.
.#4 Another framework writer has also implemented method using the private name work. The application developer decides to add that framework to the application. MF still works as does the apps own work method: var MF = InstallMF(); var BF = BFLoader(); Object.prototype.work = function () { ...}; var thing = MF.getAThing(); //behind the scene calls MF's work method, still works fine var appThing = think.work(); //class the app work method. var bStuff = BF.buildStuff(); //behinds the scene calls BF's work method.
.#5 The application writer studies the document of the MF framework and discovers that to accomplish some specific goal using the framework they need to call its work method on an object. Because they are already using their own work method on the same objects they imports MF's work private name under another name of the apps choosing: var MF = InstallMF(); var BF = BFLoader(); Object.prototype.work = function () { ...}; var thing = MF.getAThing(); //behind the scene calls MF's work method, still works fine var appThing = thing.work(); //class the app work method. var bStuff = BF.buildStuff(); //behinds the scene calls BF's work method. private workMF = MF.names.work; appThing.workMF(); //Explicitly MF's work method
.#6 The application writer subsequently discovers that they also need to invoke BF's work method so they also imports BFs work private name under another name of the apps choosing: var MF = InstallMF(); var BF = BFLoader(); Object.prototype.work = function () { ...}; var thing = MF.getAThing(); //behind the scene calls MF's work method, still works fine var appThing = thing.work(); //class the app work method. var bStuff = BF.buildStuff(); //behinds the scene calls BF's work method. private workMF = MF.names.work; appThing.workMF(); //Explicitly MF's work method private workBF = BF.getExtensionMethodName("work"); appThing.workBF();
Note that it is only in scenarios #5 and #6 that any sort of explicit name disambiguation needs to be done. There are situations where the app developer needs to have simultaneous access to similarly named extension methods. The disambiguation only is necessary within the local code that requires such access and the developer can choose what ever local name assignment will be most convenient and meaningful from the app perspective.
I have a few additional comments embedded below.
On Mar 19, 2011, at 10:54 PM, Andrew Dupont wrote:
On Mar 19, 2011, at 7:02 PM, Sam Tobin-Hochstadt wrote:
You're correct -- this won't do what you probably intended. But the great thing about private names is that this is a problem you can locally fix. For example:
private myClone = installCloneLibrary(); var twin = [{a:0}, {b:1}].myClone(); var thing = MyObj.clone();
Yeah, but at that point one might as well just name the function myClone (replacing "my" with some framework-specific prefix) and eschew private names altogether. In this example, being able to name the method "clone" without fear of naming collisions is the whole point.
True, the framework could have prefixed all its internal extension methods but that is not fool proof and all internal uses of the methods may look "ugly". From the framework perspective, you can view private names as a prefixing mechanism that guarantees you will never have conflicts with another frameworks prefixing. It also allows you to internally use pleasant, readable names.
or
var cloneProp = installCloneLibrary(); var twin = [{a:0}, {b:1}]cloneProp; var thing = MyObj.clone();
This is more palatable, but I wager it's far harder for end-users to grok.
Which is one of the motivation for private names. The above formulation would need to be used even when there wasn't a conflict and as you say, it is harder to grok.
Anyway, now that I've confirmed my suspicions, I'm hesitant about the private names proposal as described. The fact that declaring a certain name as private affects all property name lookups in that scope (all lookups that use the dot operator or object literal syntax, at least) — well, I'm not sure I like the implications. It would mean a new and surprising distinction between dot notation and bracket notation.
There is already a distinction between dot notation and bracket notation: var obj = {0: "zero", foo: "foo"}; var foo = 0; print ( obj.foo === obj[foo]); //false, really obj.foo ===obj[42]
[snip]
Anyway, now that I've confirmed my suspicions, I'm hesitant about the private names proposal as described. The fact that declaring a certain name as private affects all property name lookups in that scope (all lookups that use the dot operator or object literal syntax, at least) — well, I'm not sure I like the implications. It would mean a new and surprising distinction between dot notation and bracket notation.
There is already a distinction between dot notation and bracket notation: var obj = {0: "zero", foo: "foo"}; var foo = 0; print ( obj.foo === obj[foo]); //false, really obj.foo ===obj[42]
Wait, what? The correct comparison would be obj.foo === obj["foo"]
. But
perhaps something got removed -- where does obj.foo === obj[42]
even come
from?
Oops mind fart:
Make that obj.foo===obj[0]
The point is that what follows the dot ia already evaluated differently than what comes between brackets
On Mar 20, 2011, at 12:22 PM, Allen Wirfs-Brock wrote:
Oops mind fart:
Make that obj.foo===obj[0]
The point is that what follows the dot ia already evaluated differently than what comes between brackets
Right; I think Dean and I are saying that this would be the first time obj.foo meant something different from obj['foo']. And to ascertain that those two meant different things, I'd have to go searching through the code for a private foo
declaration.
On Mar 20, 2011, at 10:55 AM, Andrew Dupont wrote:
Right; I think Dean and I are saying that this would be the first time obj.foo meant something different from obj['foo']. And to ascertain that those two meant different things, I'd have to go searching through the code for a
private foo
declaration.
With the private name proposal obj.foo and obj.[#.foo] will always mean the same thing regardless of whether foo is scoped as a private name or as a regular property name. BTW, if you know that a property name is foo, why would you ever code obj["foo"] instead of obj.foo? [ ] should only be needed if the property name is not an identifier or is a computed value (hence my original example).
OK, you lost me.
On Mar 20, 2011, at 2:36 PM, Allen Wirfs-Brock wrote:
On Mar 20, 2011, at 10:55 AM, Andrew Dupont wrote:
Right; I think Dean and I are saying that this would be the first time obj.foo meant something different from obj['foo']. And to ascertain that those two meant different things, I'd have to go searching through the code for a
private foo
declaration.With the private name proposal obj.foo and obj.[#.foo] will always mean the same thing regardless of whether foo is scoped as a private name or as a regular property name.
I'm not comparing obj.foo
and obj[#.foo]
; I get that those two are equivalent for private names. (I don't know what you mean when you say those are the same for public names, though, because I don't know what obj[#.foo]
means in a public context.)
BTW, if you know that a property name is foo, why would you ever code obj["foo"] instead of obj.foo?
The proposal strongly implies that the private
declaration affects only "a property name on the right of . or the left of : in an object initialiser." Does it also affect bracket notation? In other words:
private foo; obj.foo = 42; obj['foo'] === obj.foo; // true or false?
If the answer is false
, that's your answer for why I'd ever code obj['foo']
instead of obj.foo
. If the answer is true
, then that answers one of the questions I was asking earlier; but it also means that there's no way to get around the fact that the private
declaration is "shadowing" any possible use of a public property name called foo
in the same lexical scope.
On Sun, Mar 20, 2011 at 3:36 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:
On Mar 20, 2011, at 10:55 AM, Andrew Dupont wrote:
Right; I think Dean and I are saying that this would be the first time obj.foo meant something different from obj['foo']. And to ascertain that those two meant different things, I'd have to go searching through the code for a
private foo
declaration.With the private name proposal obj.foo and obj.[#.foo] will always mean the same thing regardless of whether foo is scoped as a private name or as a regular property name. BTW, if you know that a property name is foo, why would you ever code obj["foo"] instead of obj.foo? [ ] should only be needed if the property name is not an identifier or is a computed value (hence my original example).
In order to demonstrate the obj["foo"] === obj.foo
equality you're example
would need to set the foo identifier to "foo" instead of 0. I don't know if
this behavior is explicitly promised as an invariant by the language but
it's certainly something we've all come to expect. I remember this coming up
once before WRT private names -- perhaps the benefits outweigh the costs of
breaking this expectation, but it's definitely surprising.
On Sun, Mar 20, 2011 at 3:49 PM, Andrew Dupont <mozilla at andrewdupont.net> wrote:
BTW, if you know that a property name is foo, why would you ever code obj["foo"] instead of obj.foo?
The proposal strongly implies that the
private
declaration affects only "a property name on the right of . or the left of : in an object initialiser." Does it also affect bracket notation? In other words:private foo; obj.foo = 42; obj['foo'] === obj.foo; // true or false?
Assuming that we hadn't previously set obj['foo'], then false.
If I can add my point to the discussion, I must admit I don’t like the “Private Names Proposal” either. I find it confusing and complex. As I thought I was the only one who had this in mind, I didn’t react before but if more people are confident it’s a strange proposal, maybe could the proposal be replace by something simpler and more intuitive.
When I first read the proposal, I thought we may use a new property of the property descriptors to define the “privality” of a property.
What I propose is to manage a list of all currently defined private keys. This list is managed scope by scope. Each time a <<private key>> instruction is encountered, the value of the private key that follows (or, if undefined, a new private key) is added to private keys list (shortened PKL further in the mail).
Then, I propose to add a new property to each property descriptor: privateKey. When a property is read or modified somewhere in the code, the operation can only complete if the value of getOwnProprtyDescriptor(currentProperty).privateKey is in the PKL. It will fails otherwhise (return undefined for a read operation or throw an error if an attempt is made to change its value).
{
private key ModulePrivate; // generate a new private key named ModulePrivate, and add it to the current scope’ PKL
// Create a new global property called “doSomethingPrivate” and only accessible to scopes who have the “ModulePrivate” key
<private: ModulePrivate> function doSomethingPrivate() { ... }
// Create a new global property called “getModulePrivateKey” accessible to every scope
function getModulePrivateKey() { return ModulePrivate; }
doSomethingPrivate(); // works
/* As the interpreter goes out of the scope where ModulePrivate is defined, ModulePrivate is removed from the PrivateKeys collection */
}
try { doSomethingPrivate(); } catch (ex) {} // throw a “undefined is not an object” exception as doSomethingPrivate returned ‘undefined’ (ModulePrivate is not in the PKL)
private key ModulePrivate = getModulePrivateKey(); // private key += getModulePrivateKey() would works, too (in case you don’t need a reference to the key)
try { doSomethingPrivate(); } catch (ex) {} // works
The only feature that disappeard is the ability to define more than one property having the same “apparent” name.
I personnally feel this was a bad practice, anyway. I don’t like the fact a property could change of name in function of the scope. And I find it even worse that two properties sharing the same name in two different scopes can’t share the same name in the same scope because the “property name” is become a variable and you can’t have two variables with the same name.
To make my point clear, here an example (for the purpose of the demonstration, I want to define a private method (clone) in the Obj class that can only be accessed from within the own Obj instance or by a SharedClass instance, but not within another Obj instance) :
{
private getPrivateName;
function SharedClass(a, b) {
private clone = a.getPrivateName();
private clone2 = b.getPrivateName(); // #fail: it’s the same method, but I had to choose a different name!
this.c = a.clone();
this.d = b.clone2();
}
function Obj() {
private clone;
this.getPrivateName = function() { return #.clone; }
this.clone = function() { ... }
}
}
Using my proposal, it would be cleaner :
{
private key SharedProp;
function SharedClass(a, b) {
private key += a.getPrivateName();
private key += b.getPrivateName();
this.c = a.clone();
this.d = b.clone(); // same name
}
function Obj() {
private key ObjInstancePrivate;
this.<private: ObjInstancePrivate>clone = function() { ... }
this.<private: SharedProp>getPrivateName = function() {
return ObjInstancePrivate;
}
}
}
The problem shown before can be even worse: imagine you have to work on objects in a loop, you can’t import all property names and you’ll be forced to use the [] notation in your code. It’s not the case using my proposal since you can add “anonymous” private names to the scope’ PKL.
My proposal is also simpler in the sense you don’t need to modify the fact that a property name has to be a String. It also make runtime optimisation easier to find out since you don’t need to know which ‘private names’ are loaded because only one property could have the “clone” name on the object, anyway (that the property read/write will throw an error or not hasn’t to be known at compilation time).
Any thought on this ? , François
From: Andrew Dupont Sent: Sunday, March 20, 2011 6:54 AM To: Sam Tobin-Hochstadt Cc: es-discuss at mozilla.org Subject: Re: About private names
On Mar 19, 2011, at 7:02 PM, Sam Tobin-Hochstadt wrote:
You're correct -- this won't do what you probably intended. But the great thing about private names is that this is a problem you can locally fix. For example:
private myClone = installCloneLibrary(); var twin = [{a:0}, {b:1}].myClone(); var thing = MyObj.clone();
Yeah, but at that point one might as well just name the function myClone (replacing "my" with some framework-specific prefix) and eschew private names altogether. In this example, being able to name the method "clone" without fear of naming collisions is the whole point.
or
var cloneProp = installCloneLibrary(); var twin = [{a:0}, {b:1}]cloneProp; var thing = MyObj.clone();
This is more palatable, but I wager it's far harder for end-users to grok.
Anyway, now that I've confirmed my suspicions, I'm hesitant about the private names proposal as described. The fact that declaring a certain name as private affects all property name lookups in that scope (all lookups that use the dot operator or object literal syntax, at least) — well, I'm not sure I like the implications. It would mean a new and surprising distinction between dot notation and bracket notation.
As a maintainer of a framework that does quite a bit of built-in extension, I can't imagine us using private names for this purpose. If we wanted to define {}.clone in this manner, we'd be exchanging one potential collision (one property defined in one place) for another (every property defined in the same lexical scope). If the private names had their own operator (e.g., twin#clone vs. twin.clone), it'd at least be worth considering.
I'm sure private names would be useful for other reasons, but IMO it wouldn't solve the problem of safely extending built-ins.
On Mar 20, 2011, at 12:49 PM, Andrew Dupont wrote:
OK, you lost me.
On Mar 20, 2011, at 2:36 PM, Allen Wirfs-Brock wrote:
On Mar 20, 2011, at 10:55 AM, Andrew Dupont wrote:
Right; I think Dean and I are saying that this would be the first time obj.foo meant something different from obj['foo']. And to ascertain that those two meant different things, I'd have to go searching through the code for a
private foo
declaration.With the private name proposal obj.foo and obj.[#.foo] will always mean the same thing regardless of whether foo is scoped as a private name or as a regular property name.
I'm not comparing
obj.foo
andobj[#.foo]
; I get that those two are equivalent for private names. (I don't know what you mean when you say those are the same for public names, though, because I don't know whatobj[#.foo]
means in a public context.)
From the Private Names Strawman: The syntactic form is #. IdentifierName. This may be used as a PrimaryExpression and yields the property name value of the IdentifierName. This may be either a private name value or a string value, depending upon whether the expression is within the scope of a private declaration for that IdentifierName;
BTW, if you know that a property name is foo, why would you ever code obj["foo"] instead of obj.foo?
The proposal strongly implies that the
private
declaration affects only "a property name on the right of . or the left of : in an object initialiser." Does it also affect bracket notation? In other words:private foo; obj.foo = 42; obj['foo'] === obj.foo; // true or false?
If the answer is
false
, that's your answer for why I'd ever codeobj['foo']
instead ofobj.foo
. If the answer istrue
, then that answers one of the questions I was asking earlier; but it also means that there's no way to get around the fact that theprivate
declaration is "shadowing" any possible use of a public property name calledfoo
in the same lexical scope.
as Sam answered, the answer is false.
// test if a property name is a private name in the current scope: if (#'foo==='foo') { //foo is bound when used as a property name to its default string value, not a private name value } else { //foo has a private name binding }
Yes, I appreciate that if private names existed you might use obj['foo'] to guarantee you were accessing a string-named property. However, my original comment was in response to your statement "It would mean a new and surprising distinction between dot notation and bracket notation." I was trying to argue (apparently not very effectively) that there was already a significant distinction between dot notation and bracket notation and that private name is just building upon that distinction. Without private names there is no particular reason to say obj['foo'] rather than obj.foo but there is a very important distinction between obj[foo] and obj.foo. The private names proposal preserves that distinciton.
BTW, if you know that a property name is foo, why would you ever code obj["foo"] instead of obj.foo?
The most obvious reason is if the name of the property contains a character which cannot be an identifier character in the property name... like a unicode character, for instance.
Without private names there is no particular reason to say obj['foo'] rather than obj.foo but there is a very important distinction between obj[foo] and obj.foo.
Are we talking about the difference between obj['foo'] and obj[foo]? I think perhaps that was a subtle shift in this conversation that I missed until just now?
Without private names, is there (and what is it?) an important distinction between:
- obj[foo] and obj.foo; AND
- obj['foo'] and obj.foo
On Sun, Mar 20, 2011 at 6:21 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:
On Mar 20, 2011, at 12:49 PM, Andrew Dupont wrote:
OK, you lost me.
On Mar 20, 2011, at 2:36 PM, Allen Wirfs-Brock wrote:
On Mar 20, 2011, at 10:55 AM, Andrew Dupont wrote:
Right; I think Dean and I are saying that this would be the first time obj.foo meant something different from obj['foo']. And to ascertain that those two meant different things, I'd have to go searching through the code for a
private foo
declaration.With the private name proposal obj.foo and obj.[#.foo] will always mean the same thing regardless of whether foo is scoped as a private name or as a regular property name.
I'm not comparing
obj.foo
andobj[#.foo]
; I get that those two are equivalent for private names. (I don't know what you mean when you say those are the same for public names, though, because I don't know whatobj[#.foo]
means in a public context.)From the Private Names Strawman: The syntactic form is #. IdentifierName. This may be used as a * PrimaryExpression* and yields the property name value of the * IdentifierName*. This may be either a private name value or a string value, depending upon whether the expression is within the scope of a private declaration for that IdentifierName;
BTW, if you know that a property name is foo, why would you ever code obj["foo"] instead of obj.foo?
The proposal strongly implies that the
private
declaration affects only "a property name on the right of . or the left of : in an object initialiser." Does it also affect bracket notation? In other words:private foo; obj.foo = 42; obj['foo'] === obj.foo; // true or false?
If the answer is
false
, that's your answer for why I'd ever codeobj['foo']
instead ofobj.foo
. If the answer istrue
, then that answers one of the questions I was asking earlier; but it also means that there's no way to get around the fact that theprivate
declaration is "shadowing" any possible use of a public property name calledfoo
in the same lexical scope.as Sam answered, the answer is false.
// test if a property name is a private name in the current scope: if (#'foo==='foo') { //foo is bound when used as a property name to its default string value, not a private name value } else { //foo has a private name binding }
Yes, I appreciate that if private names existed you might use obj['foo'] to guarantee you were accessing a string-named property. However, my original comment was in response to your statement "It would mean a new and surprising distinction between dot notation and bracket notation." I was trying to argue (apparently not very effectively) that there was already a significant distinction between dot notation and bracket notation and that private name is just building upon that distinction. Without private names there is no particular reason to say obj['foo'] rather than obj.foo but there is a very important distinction between obj[foo] and obj.foo. The private names proposal preserves that distinciton.
I think you're missing the distinction. The obj["foo"] example is just a
stand-in for var foo="foo"; obj[foo]
-- we would all expect the string key
lookup to be the same as obj.foo, and anything less would be a bit
surprising. This is what Andrew was reacting to.
The relationship between bracketed string lookup and dot lookup is, for now, very clear and predictable -- private names muddies it up a bit. Perhaps a big bold warning about this is enough to realign developer expectations, I couldn't say. But whether you'd ever do a bracketed string-literal lookup is beside the point.
On Mar 20, 2011 3:34 PM, "Kyle Simpson" <getify at gmail.com> wrote:
BTW, if you know that a property name is foo, why would you ever code
obj["foo"] instead of obj.foo?
The most obvious reason is if the name of the property contains a
character which cannot be an identifier character in the property name... like a unicode character, for instance.
"Unicode characters" (non-ASCII) other than whitespace are pretty much all permissible in property names via dot syntax, FWIW. Lambda and pi are the most common, though I've seen other math/greek as well.
A property name with whitespace is not expressible with dot syntax, but unlikely to be used except in object-as-hashtable cases.
Are we talking about the difference between obj['foo'] and obj[foo]? I
think perhaps that was a subtle shift in this conversation that I missed until just now?
Without private names, is there (and what is it?) an important distinction
between:
- obj[foo] and obj.foo; AND
- obj['foo'] and obj.foo
The former case of #1 refers to a property named by the value of the variable |foo|. All other cases refer to a property named "foo". So the cases in #2 are equivalent, but the ones in #1 are not.
Mike
On Mar 20, 2011, at 4:20 PM, Dean Landolt wrote:
[big top-cite snipped -- folks, please try to cut if you can.]
I think you're missing the distinction. The obj["foo"] example is just a stand-in for
var foo="foo"; obj[foo]
-- we would all expect the string key lookup to be the same as obj.foo, and anything less would be a bit surprising. This is what Andrew was reacting to.
I see confusion here in making the name of the var the same as the string contents.
Allen's point is valid, I've meditated on it at length:
In JS, a plain identifier not after dot is looked up in the scope chain (in Harmony this will be lexical scope only, i.e., all compile time -- typos get early errors).
But after dot, an identifier is not looked up early or late in the scope chain, only in the proto chain of the object resulting from evaluating the expression to the left of the dot.
Obviously, obj.foo and obj[id] are equivalent currently iff id === "foo". In particular, this equivalence does not apply if id's character sequence does not match the lexical grammar for Identifier.
Private names indeed adds indirection, but we already have the public (string-equated) name case: it must be lexically an identifier.
The indirection due to the |private id| declaration, block scoped, may be confusing or helpful. The strawman shows examples that can be read both ways.
The relationship between bracketed string lookup and dot lookup is, for now, very clear and predictable -- private names muddies it up a bit.
I wouldn't say "muddle", given the existing "public name must be an identifier, lexically" restriction.
The strawman proposes the #.id operator to reify a private name as a value to be used in brackets, or a public name as a string, also usable in brackets.
There's no free lunch, so you could claim this whole strawman is too expensive. Compared to what, though? Without this tool, name-mangling has zero integrity. Anyone can step on your prefixed or otherewise mangled name.
I encourage folks to read the private names thread from late last year, since we are revisiting the points made then:
On Mar 20, 2011, at 4:20 PM, Dean Landolt wrote:
I think you're missing the distinction. The obj["foo"] example is just a stand-in for
var foo="foo"; obj[foo]
-- we would all expect the string key lookup to be the same as obj.foo, and anything less would be a bit surprising. This is what Andrew was reacting to.The relationship between bracketed string lookup and dot lookup is, for now, very clear and predictable
I don't see any dot lookup above so I'm not sure how the above assertion follows form the example. In general if you see obj[foo] and obj.foo without knowing the value of the variable foo you can't make any assumptions about the whether or not they will access the same property.
Private names changes nothing about this. If you know the value of a variable or expression used to produce a property lookup key then you know what property will be access. If you don't know what value is going to be produce then you cant make any correlation to a property access using a known literal value.
-- private names muddies it up a bit. Perhaps a big bold warning about this is enough to realign developer expectations, I couldn't say. But whether you'd ever do a bracketed string-literal lookup is beside the point.
The most important part of the private name proposal is that it expands the domain of value that can be used as property keys to include certain non-string values. This these are property names that do not have literal representations either as quoted strings or identifiers that are treated as literal string value.
This could be supported without any changes to the surface syntax or semantics of the core language via a built-in constructor:
var nonStringName1 = new PrivateName; //generate a unique property key
var obj= { }; obj[nonStringName1] = 1; print(obj[nonStringName1]); //1 print(obj["nonStringName1"); //undefined functionDefinedElseWhere(obj); // won't be able to access the property because it doesn't know its name
We could stop here and some may reasonably argue that we should. The rest of the private names proposal is about enabling the use of dot notation with of non-string valued private names and also (and probably more important) the definition of properties wit such names in object literals.
First of all, let me be clear: my objections are only as deep as the syntax layer. I'm not arguing against the usefulness of private names as a concept.
On Mar 20, 2011, at 6:51 PM, Brendan Eich wrote:
I encourage folks to read the private names thread from late last year, since we are revisiting the points made then:
OK, I read through the December thread. Let me pull out some of the highlights:
David-Sarah Hopwood said:
I don't like the private names syntax. I think it obscures more than it helps usability, and losing the x["id"] === x.id equivalence is a significant loss.
This is exactly how I feel. Yes, I realize that these aren't exactly equivalent, so let me state it another way: right now, bracket notation is a superset of dot notation, but it would no longer be under the proposed syntax. I, too, feel that that's a significant loss in simplicity and isn't worth the trade-off.
Brendan said:
That is, without private_names, the "p" in the expression |o.p| is literal text, not lexically bound as it could be via a prior |private p|.
This gets at my other objection. Code is far harder to debug when every single property lookup could have a meaning other than its plain appearance, depending on the existence of private
declarations earlier in the scope.
Allen Wirfs-Brock said:
This is a useful line of discussion. Up to this point there has been quite a bit of "I don't like the syntax in the private names proposal" but not a lot of suggested alternatives.
My request to anyone who wants some sort of private object state access is: propose the syntax you want to use.
My suggestion would be to remove private
's ability to affect dot notation and object literals in any way that can affect users' existing JavaScript knowledge. In other words, the current syntax changes the meaning of the simple Foo.bar
that we're all accustomed to writing; this wouldn't be an issue if new syntax were involved.
For example, here's an altered version of the first example in the strawman, with comments to indicate what I've changed:
function makeObj() {
private unique;
var obj = {};
obj[#.unique] = 42; // was `obj.unique = 42;`
print(obj[#.unique]); // 42
print(obj.unique); // undefined (was 42)
return obj;
}
var obj = makeObj();
print(obj["unique"]); // undefined -- the name of the property is still not the string "unique"
print(obj.unique); // (ditto)
print(obj[#.unique]); // undefined -- we're in a different scope
This preserves the existing behavior of dot notation (as a literal property lookup by IdentifierName) and extends the behavior of bracket notation in a logical manner (as a superset of dot notation).
(I also would have no problem with the completely desugared approach — replacing the private
directive with something like const unique = new Name()
as has been proposed — but I don't want to paint this as a sugar/no-sugar dichotomy.)
Allen had another concern, though:
However, a consequence of eliminating the private declaration is that we also eliminate the obvious way to extend object literals to support private properties
As I see it, that could be solved with the same #.foo syntax:
function makeObj() {
private unique;
var obj = { #.unique: 42 };
// et cetera
}
Another alternative, one I briefly mentioned earlier, would be to use # as a private-name equivalent of . (e.g., obj#unique instead of obj.unique). It seems like this would be a larger can of worms to open, but I mention it again in the spirit of brainstorming.
Again: private names, as a language feature, seem like a fine idea; but, while I am not at all opposed to introducing new syntax, I find the existing syntax proposal for private names would introduce alarming new complexity.
right now, bracket notation is a superset of dot notation, but it would no longer be under the proposed syntax.
I'm afraid I can't figure out what this means, but it doesn't sound true to me.
This gets at my other objection. Code is far harder to debug when every single property lookup could have a meaning other than its plain appearance, depending on the existence of
private
declarations earlier in the scope.
OK, we've heard this point a number of times at this point. I'm the original proposer of the private names strawman, so I haven't been particularly sympathetic to this critique. But it's been the reaction of several people when I've described the proposal, so it's pretty clearly worth taking seriously.
Let me try to see if I can construct examples where this might be a problem. Here's one involving a name collision:
private draw; // for the cowboy's internal draw method
function Cowboy(...) { ... }
Cowboy.prototype = {
// private method
draw: function() { ... },
otherMethod: function() { ... this.draw() ... },
...
};
...
var shape = ...;
shape.draw(); // oops, this was supposed to be the public "draw" method
On the one hand, my feeling has been that programmers are generally in control of what they have in scope. (It isn't specified in the proposal, but I might suggest that it shouldn't be allowed to make private declarations dynamically in global scope.) But that doesn't mean that you can't still trip over it yourself, or that you could find yourself in a reasonably deep scope, and not notice that a name has been declared private.
So I guess I'm beginning to find the objections persuasive.
But when you propose only using the bracket notation, I think you lose something important that came with the private names proposal. The importance of the dot-notation is to distinguish the static case from the dynamic case. The dot-notation says "I know what name I'm dealing with." Now granted, the private declaration indicates a dynamic generation of a fresh name, but it still can be tied statically to the program point at which the private declaration was made.
So here's an alternative I've been kicking around lately. The private declaration is essentially as before, but we have a variation on dot-notation that is syntactically distinct from ordinary dot-notation, and can only take a private name that's in scope:
private #draw;
function Cowboy(...) { ... }
Cowboy.prototype = {
// private method
#draw: function() { ... },
otherMethod: function() { ... this.#draw() ... },
...
};
...
var shape = ...;
shape.draw(); // this has to be the public draw method
So now you can always syntactically tell whether you're referring to a private name. You still have to know what private declaration you're getting, but you can't get confused about whether you're referring to a public vs. private name. At the same time, you still get to use dot-notation, so that you can tell that this is a case where the name's provenance is statically known.
I would still want the ability to promote a private name to its runtime value in an expression, so that you could do something like:
friend.drawCapability = #draw;
to dynamically share a private name as needed.
Again: private names, as a language feature, seem like a fine idea; but, while I am not at all opposed to introducing new syntax, I find the existing syntax proposal for private names would introduce alarming new complexity.
Hm, it didn't really sound like complexity that you were objecting to, so much as the danger of confusion caused by having the dot-notation be overloaded. I don't really see the private names proposal as very complex. It seems like a fairly conservative extension of the JS object-property model in order to enable encapsulation, which is something many (not all, but many) people miss. In fact, as we've talked at Mozilla about the very real possibility of implementing portions of the DOM in JS rather than C++, we need some encapsulation mechanism with better performance than weak maps.
(Incidentally, this is all orthogonal to the separate question of whether private names should offer strong/true encapsulation. So if we can, let's avoid getting into that issue in this thread.)
Does my point about dot-notation make sense to you? What do you think of the above revision of the proposal?
On Mar 20, 2011, at 11:05 PM, David Herman wrote:
right now, bracket notation is a superset of dot notation, but it would no longer be under the proposed syntax.
I'm afraid I can't figure out what this means, but it doesn't sound true to me.
I noticed (in both this thread and the December thread) a back-and-forth where one person said that obj.foo
and obj['foo']
were equivalent, and a second person replied to say, no, they're not exactly equivalent. I was trying to bypass that quibble.
Right now, everything that can be expressed via dot notation has an analog in bracket notation that means the same thing. Bracket notation can additionally accommodate any string that doesn't qualify as an IdentifierName. That's what I meant by "superset."
Hm, it didn't really sound like complexity that you were objecting to, so much as the danger of confusion caused by having the dot-notation be overloaded.
Either phrasing is accurate.
Does my point about dot-notation make sense to you? What do you think of the above revision of the proposal?
The revision would address all my concerns — thanks. I'm all for distinguishing the static case from the dynamic case, as long as we can maintain the separation between public names and private names, and know which is which without indirection.
On Mar 20, 2011, at 9:58 PM, Andrew Dupont wrote:
The revision would address all my concerns — thanks. I'm all for distinguishing the static case from the dynamic case, as long as we can maintain the separation between public names and private names, and know which is which without indirection.
Can you explain why you feel this is important. I note that it is not a characteristic of some of the most common used object oriented, even those that support explicit member visibility declarations. more concretely, if I'm examine a line of Java code that reads like: this.doSomething(); there is nothing explicit that tell me the visibility of doSomeThing or that it is referring to a member that perhaps has different visibility than what is referenced by: that.doSomething();
Is there any evidence that this is widely know to create confusion in such languages. I do know that some people adopt naming conventions to explicitly designate such attributes of names. Personally, I have generally found such conventions to be a distraction. However, there is nothing that would prevent a JavaScript developer from apply such conventions to private names if they found them useful.
In general, why do I even care about such visibility at a referencing site. At such a point I either do or don't have visibility of the property. If I do have visibility then why do I care whether or not if anyone else has visibility. Isn't that more a concern for the definer of a property rather than a consumer?
A separate point is that requiring such designation at every usage points makes it significantly more difficult to switch from public to private visibility or visa versa. And hence may tend to prematurely lock in such design decisions.
On Mar 21, 2011, at 12:50 AM, Allen Wirfs-Brock wrote:
On Mar 20, 2011, at 9:58 PM, Andrew Dupont wrote:
The revision would address all my concerns — thanks. I'm all for distinguishing the static case from the dynamic case, as long as we can maintain the separation between public names and private names, and know which is which without indirection.
Can you explain why you feel this is important. I note that it is not a characteristic of some of the most common used object oriented, even those that support explicit member visibility declarations. more concretely, if I'm examine a line of Java code that reads like: this.doSomething(); there is nothing explicit that tell me the visibility of doSomeThing or that it is referring to a member that perhaps has different visibility than what is referenced by: that.doSomething();
Java doesn't formalize this, you're right — but if I'm looking at this.doSomething()
and wondering about its visibility, I can look in the class definition itself (to see if it's private) or in one of its superclasses (to see if it's protected). Java does formalize the locations of class files, so I know exactly where to look to get my answer. I think that's a far smaller area to have to search than an entire lexical scope.
Two minor points, though: (a) many coding styles notate private members with underscores, so that it's obvious at a glance what is private and what is not; (b) many Java authors use "heavy" IDEs which can settle the question without sending the author off on a hunt (in Eclipse, it just requires hovering over the identifier, IIRC). JavaScript authors wouldn't necessarily be able to rely on that level of help from their editors.
And one more major point: Java's visibility rules differ in important ways. It is true that I can't define a public doSomething
method and a private doSomething
method in the same class, but that collision is only within the class itself. Yet under the proposed syntax, if I introduce doSomething
as a private name in any scope, that prevents me from referring to anything with that public name in the same lexical scope. It'd be like Java telling me I couldn't call someObject.doSomething()
within a class because I had declared my own personal unrelated doSomething
method as private.
My main objection is the idea of private names that "shadow" public identifiers. The point about indirection is secondary — but also important, IMO, because the proposed syntax would be introducing indirection in a place where there was none previously. I liked David Herman's proposal because it addressed both parts.
After seeing this thread I really though it's important to share my thought on this as well. So when I first sew this proposal my reaction was similar to Andrew's. It took me a while to realize that issues with shadowing were not as big as they felt at first. In fact only case where shadowing may affect you unintentionally is when it's hang out in the global scope, but again modules and other changes to ES.next are making it non issue. Also it's worth noticing that today all the frameworks define themself in their own lexical scopes (self executable functions).
Also I think this proposal won't be really useful without syntax sugar. To elaborate more, I think this is a perfect feature for libraries that extend built-ins like prototype, or mootools or their extensions may be defined as private names that consumers may decide to use by importing names, or rename or just ignore entirely.
I would encourage to writing examples of where name shadowing may cause confusions, and iterate on them :)
Also I think there is one thing (that may not be an issue either) that may be improved:
var foo = someModule.bar();
It's hard to know what foo
is on the next line, as it may be a private
name that will change behavior of lexical scope or it maybe just a value
returned by function, even worth doe the bug in bar
it may end up being a
private name instead of variable that will completely break my program.
Would be nice to have a better control of that. For example
private foo = someModule.bar();
would be explicit, also will make it way more simple to inspect source to see what names are shadowed.
-- Irakli Gozalishvili Web: www.jeditoolkit.com Address: 29 Rue Saint-Georges, 75009 Paris, France goo.gl/maps/3CHu
On Mar 20, 2011, at 11:30 PM, Andrew Dupont wrote:
Java doesn't formalize this, you're right — but if I'm looking at
this.doSomething()
and wondering about its visibility, I can look in the class definition itself (to see if it's private) or in one of its superclasses (to see if it's protected). Java does formalize the locations of class files, so I know exactly where to look to get my answer. I think that's a far smaller area to have to search than an entire lexical scope.
Hi Andrew, thanks for the good thinking on this thread. I'm not cherry-picking here, but I wanted to reply to the specific sentence ending "entire lexical scope".
Is a lexical scope bigger and harder to search than two or more class files in Java? Ignoring IDEs, this seems to suggest lexical scopes tend to be "big". Is this based on your experience with JS blocks (not function bodies or global scopes) today?
The Harmony top-level scope (no global object!) will be big and it may even grow by (non-colliding) extension, sure. Hence Dave's thought of not allowing private there.
Anyway, I wanted to check this (what I took to be your own experience-based) weighting of search space sizes.
right now, bracket notation is a superset of dot notation, but it would no longer be under the proposed syntax.
I'm afraid I can't figure out what this means, but it doesn't sound true to me.
Right now, everything that can be expressed via dot notation has an analog in bracket notation that means the same thing.
This is also true of the private names proposal with the overloaded dot notation. Given any expression <<expr>>.foo, the expression is equivalent to <<expr>>[#.foo]. (If `foo' is not private, then the expression is also equivalent to <<expr>>['foo'] as usual.)
Bracket notation can additionally accommodate any string that doesn't qualify as an IdentifierName. That's what I meant by "superset."
And that would continue to be true as well.
So I still don't see how that's an argument against private dot-notation.
Brendan and Irakli both beat me to the punch here -- I would really like to see stronger evidence that "an entire lexical scope" is really so onerous. Everything you say about how Java mitigates the problem is just as applicable to Harmony.
Java doesn't formalize this, you're right — but if I'm looking at
this.doSomething()
and wondering about its visibility, I can look in the class definition itself (to see if it's private) or in one of its superclasses (to see if it's protected). Java does formalize the locations of class files, so I know exactly where to look to get my answer.
So does Harmony. You can find where everything is defined, so there's no question as to whether something is declared to be private.
I think that's a far smaller area to have to search than an entire lexical scope.
In Java it is, in fact, more than an entire lexical scope. A private field is lexically scoped, but it can also be called on an object, so you also have to look up the type definition.
Two minor points, though: (a) many coding styles notate private members with underscores, so that it's obvious at a glance what is private and what is not;
...So, JS programmers could do the same thing.
(b) many Java authors use "heavy" IDEs which can settle the question without sending the author off on a hunt (in Eclipse, it just requires hovering over the identifier, IIRC). JavaScript authors wouldn't necessarily be able to rely on that level of help from their editors.
There's no reason why they couldn't. JS editors are getting better and better (see, for example, the Ace, CodeMirror, and Orion editors), and because this is dependent only on lexical scope, and not for example on type information which JS editors don't have, it's really easy to for an editor to implement.
And one more major point: Java's visibility rules differ in important ways. It is true that I can't define a public
doSomething
method and a privatedoSomething
method in the same class, but that collision is only within the class itself. Yet under the proposed syntax, if I introducedoSomething
as a private name in any scope, that prevents me from referring to anything with that public name in the same lexical scope.
This is true. But because you control the local view of the private name, and that local binding is not exposed to anyone else, you can always rename your local name out of the way. For example, the Cowboy/Shape example I gave previous could safely be rewritten:
private drawGun;
...
Cowboy.prototype = {
drawGun: function() { ... },
...
};
var shape = ...;
shape.draw();
and now there's no conflict. Renaming the local binding from draw to drawGun doesn't change anything about the private name itself, since at runtime it's just a fresh key. The local name is just a local view of the private key.
I'm a bit on the fence here, but Allen raised very good points about refactorability, and Irakli raise good doubts about how much your concern is likely to be a problem in practice. This isn't an obvious design decision, and rather than treat it as a binary between right and wrong, I would urge you to try to think about compelling evidence of practical hazards. AFAICT, the biggest hazard is in forgetting that something is bound as a private name and accidentally referring to a public name as a private one. But how common would this be? How often would people really bind private names in large scopes? I honestly -- in good faith -- don't know.
On Mar 21, 2011, at 11:14 AM, Brendan Eich wrote:
Hi Andrew, thanks for the good thinking on this thread. I'm not cherry-picking here, but I wanted to reply to the specific sentence ending "entire lexical scope".
The Harmony top-level scope (no global object!) will be big and it may even grow by (non-colliding) extension, sure. Hence Dave's thought of not allowing private there.
Anyway, I wanted to check this (what I took to be your own experience-based) weighting of search space sizes.
This is a good point — I'm imagining lexical scopes that, at their largest, can span the entirety of a very large library like Prototype or jQuery.
In recent years, Prototype has moved toward defining each of its sections in a separate anonymous function, declaring named functions inside of it, then selectively "exporting" some of them to the global scope. This is an increasingly common trend. In the future, I can see us moving toward a system where the whole library follows that pattern — one giant anonymous function surrounding the whole thing.
Not allowing private in the top-level scope would help a bit (at the cost of hindering the "Conflict-Free Object Extension Using Private Names" use case), but any lexical scope can grow to a staggering size.
On Mar 21, 2011, at 11:14 AM, Brendan Eich wrote:
Is a lexical scope bigger and harder to search than two or more class files in Java? Ignoring IDEs, this seems to suggest lexical scopes tend to be "big". Is this based on your experience with JS blocks (not function bodies or global scopes) today?
Until now, yes, I have been imagining a function body as an example of a lexical scope. Why are you excluding it?
On Mar 21, 2011, at 3:11 AM, Irakli Gozalishvili wrote:
Also I think this proposal won't be really useful without syntax sugar. To elaborate more, I think this is a perfect feature for libraries that extend built-ins like prototype, or mootools or their extensions may be defined as private names that consumers may decide to use by importing names, or rename or just ignore entirely.
I'm co-maintainer of Prototype and I'm saying I can't imagine we'd use this feature under the proposed syntax. And — again — I'm not opposed to syntactic sugar in general, only to this specific syntax.
On Mar 21, 2011, at 11:50 AM, David Herman wrote:
This is true. But because you control the local view of the private name, and that local binding is not exposed to anyone else, you can always rename your local name out of the way.
People keep saying this. It may be true, but it undermines one of the stated goals of the proposal. The whole point of Prototype's built-in extension is to introduce more intuitive ways of working with built-ins and to fill perceived gaps. Naming is a large part of that. {}.myClone
is, in my view, vastly inferior to {}.clone
because of its unintuitive naming. As a hypothetical consumer, I'm hardly impressed with a library that promises conflict-free usage of an instance method on objects... as long as I name the method uniquely and am careful about scoping.
On Mar 21, 2011, at 11:50 AM, David Herman wrote:
How often would people really bind private names in large scopes? I honestly -- in good faith -- don't know.
Well, again, the "Conflict-Free Object Extension Using Private Names" use case explicitly encourages using private names in large scopes. But I don't know either; I'm just trying to give my perspective as a library author. Everything about this syntax screams "footgun," both for me and for users of Prototype. Aside from writing large amounts of code with the proposed syntax, I'm not sure what I can do to illustrate this, but I'm open to ideas.
On Mar 21, 2011, at 11:29 AM, Andrew Dupont wrote:
On Mar 21, 2011, at 11:14 AM, Brendan Eich wrote:
Hi Andrew, thanks for the good thinking on this thread. I'm not cherry-picking here, but I wanted to reply to the specific sentence ending "entire lexical scope".
The Harmony top-level scope (no global object!) will be big and it may even grow by (non-colliding) extension, sure. Hence Dave's thought of not allowing private there.
Anyway, I wanted to check this (what I took to be your own experience-based) weighting of search space sizes.
This is a good point — I'm imagining lexical scopes that, at their largest, can span the entirety of a very large library like Prototype or jQuery.
They can be big (so can Java class sources). But they can be pretty small, too. Programmers get to narrow private name scope via explicit blocks, and I wasn't sure this was clear.
Not allowing private in the top-level scope would help a bit (at the cost of hindering the "Conflict-Free Object Extension Using Private Names" use case),
Not sure that is hindered much. Private names can be "consumed" as well as "produced" within block scope, too.
but any lexical scope can grow to a staggering size.
This argument goes against Java class sources, too, so to have a fair debate we should either refine it somehow, or exclude it.
On Mar 21, 2011, at 11:14 AM, Brendan Eich wrote:
Is a lexical scope bigger and harder to search than two or more class files in Java? Ignoring IDEs, this seems to suggest lexical scopes tend to be "big". Is this based on your experience with JS blocks (not function bodies or global scopes) today?
Until now, yes, I have been imagining a function body as an example of a lexical scope. Why are you excluding it?
Only to point out that private is block-scoped, like let and const (and function in block). Sure, you can use function bodies too, but you won't have to in Harmony. Currently it's all you have.
On Mon, Mar 21, 2011 at 19:29, Andrew Dupont <mozilla at andrewdupont.net>wrote:
On Mar 21, 2011, at 11:14 AM, Brendan Eich wrote:
Hi Andrew, thanks for the good thinking on this thread. I'm not cherry-picking here, but I wanted to reply to the specific sentence ending "entire lexical scope".
The Harmony top-level scope (no global object!) will be big and it may even grow by (non-colliding) extension, sure. Hence Dave's thought of not allowing private there.
Anyway, I wanted to check this (what I took to be your own experience-based) weighting of search space sizes.
This is a good point — I'm imagining lexical scopes that, at their largest, can span the entirety of a very large library like Prototype or jQuery.
In recent years, Prototype has moved toward defining each of its sections in a separate anonymous function, declaring named functions inside of it, then selectively "exporting" some of them to the global scope. This is an increasingly common trend. In the future, I can see us moving toward a system where the whole library follows that pattern — one giant anonymous function surrounding the whole thing.
Not allowing private in the top-level scope would help a bit (at the cost of hindering the "Conflict-Free Object Extension Using Private Names" use case), but any lexical scope can grow to a staggering size.
On Mar 21, 2011, at 11:14 AM, Brendan Eich wrote:
Is a lexical scope bigger and harder to search than two or more class files in Java? Ignoring IDEs, this seems to suggest lexical scopes tend to be "big". Is this based on your experience with JS blocks (not function bodies or global scopes) today?
Until now, yes, I have been imagining a function body as an example of a lexical scope. Why are you excluding it?
On Mar 21, 2011, at 3:11 AM, Irakli Gozalishvili wrote:
Also I think this proposal won't be really useful without syntax sugar. To elaborate more, I think this is a perfect feature for libraries that extend built-ins like prototype, or mootools or their extensions may be defined as private names that consumers may decide to use by importing names, or rename or just ignore entirely.
I'm co-maintainer of Prototype and I'm saying I can't imagine we'd use this feature under the proposed syntax. And — again — I'm not opposed to syntactic sugar in general, only to this specific syntax.
Andrew I think you misunderstand this feature, as prototypejs is a perfect fit for it!! What I mean is that prototype could define all the extensions names it introduces in the top of the lexical scope and export those names along with framework. That will allow consumers to choose which extensions they intend to import and which not. Some even may decide to change names (bike-shedding is very popular in JS :) Here is an example:
-- lib.js --
private clone; private extend;
Object.extend = function() {} Object.prototype.clone = function() { .... } Array.prototype.clone = function() { ... }
exports.extend = extend; exports.clone = clone;
-- consumer.js --
private clone = require("lib.js").clone; private superExtend = require("lib.js").extend
{ a: 'b' }.clone(); // { a: 'b' };
Object.extend = function () { Object.superExtend.apply(...); // do something else }
// note that extend does not overrides extend
defined by lib.js
My point is that library can uses internal names as they used it before, but if they will define their extensions as names they make them opt-in for consumers of lib.
The recent mention of the private names strawman [1] in the "Standardizing proto" thread led me to re-read the proposal. After staring at it for far too long, I had the "aha!" moment, but it left me with a question.
The "Conflict-Free Object Extension Using Private Names" section includes a code example in which a function declares a private name
clone
, uses that private name to define several extensions to built-ins, and then returns the value of the private name so that it can be passed to another scope. The outer scope uses that returned value to set the value of its own private name calledclone
, and is then able to call [].clone, {}.clone, etc., and have them point to the methods that were defined inside the function.Here's my question: what happens if, sometime after that snippet of code, a user writes code that declares a
clone
property? Something like:var MyObj = { clone: function() { return new MyObj(this); } };
In other words: it appears that, in order to use the
clone
sugar given in the example, a user must "taint" a given scope so that all further references to "clone" (after . or before : at least) will refer to that private name. That's likely not what the user intends. Once a private name is declared in a given scope, can it ever be "reclaimed" as public later on?Or, to use another example: let's say
MyObj
was defined in a different scope, one in whichclone
was assumed to be a public name. Can I do this...private clone = installCloneLibrary(); var twin = [{a:0}, {b:1}].clone(); var thing = MyObj.clone();
... and have it work the way I intend? In other words, once ES5 fails to find something with the
clone
private name defined onMyObj
, will it try to find the property with the public name ofclone
before it gives up? (If I'm reading this right, it won't.)I think it's great that we're trying to find ways to make built-in extension safer without beating the dead horse of namespaces. But I fear that the proposal, in its current form, would be too cumbersome for that purpose.
Cheers, Andrew
[1] strawman:private_names