proposal-class-fields
proposal-class-fields copied to clipboard
[Suggestion] Consider to use time proof solution with private and protected keywords
Hi,
From my point of view it seems like mistake to use # for private fields, because it is not extensible ... If in future we would want to use protected field, it will make harder to implement it ... -# ??!
I think we should not reinvent the wheel and just to use time proof solution with private and protected keywords ;)
Please see the FAQ.
@bakkot I know it's been discussed to death, that's why I'm not going to argue with you. However, if at all possible, I would like an explanation. From the FAQ:
Why aren't declarations private x?
This sort of declaration is what other languages use (notably Java), and implies that access would be done with this.x.
The part in bold is where I've always had issue. ES is a language where both a.b and a["b"] have always meant "public access". Why would the introduction of private x in such a language suddenly cause a.x to refer to private access? To my understanding, such an implication would only be possible if the presence of private x also restricted the possibility of a "public" x declaration. Since that is not the case (due to the desire for full encapsulation), on what grounds is it reasonable to claim the implication in the FAQ? I already understand the argument that this is how it is in other languages supporting the use of private x, however I believe that argument invalid as in those languages, the aforementioned restriction exists as well.
As I stated before, I will not argue. I'm only looking for the justification for this supposed implication.
I think your questions are about “why would seeing this.x make someone think it’s private” - which i agree, It wouldn’t! To me, it’s that seeing “private x” would suggest to someone that they could access the private data with “this.x”, which of course they couldn’t, which would be very confusing.
@ljharb
think your questions are about “why would seeing this.x make someone think it’s private”
Um, no. My question is exactly as I stated it. It's about why "private x implies this.x", especially given that both a private member and a public member can be defined for the same instance object. Since it's already set in stone that this.x is public, and that will never change without breaking the language, why would any new notation suddenly suggest that this.x can be non-public?
@rdking because i don't think people think about public/private in that way. I think if they see private x inside a class, they'll assume that this.x = privateData is setting the data privately. The risk of doing this is that private information could be easily made public, which can have disastrous encapsulation consequences.
@bakkot I know it's been discussed to death, that's why I'm not going to argue with you. However, if at all possible, I would like an explanation. From the FAQ:
Why aren't declarations
private x?This sort of declaration is what other languages use (notably Java), and implies that access would be done with
this.x.The part in bold is where I've always had issue. ES is a language where both
a.banda["b"]have always meant "public access". Why would the introduction ofprivate xin such a language suddenly causea.xto refer to private access? To my understanding, such an implication would only be possible if the presence ofprivate xalso restricted the possibility of a "public"xdeclaration. Since that is not the case (due to the desire for full encapsulation), on what grounds is it reasonable to claim the implication in the FAQ? I already understand the argument that this is how it is in other languages supporting the use ofprivate x, however I believe that argument invalid as in those languages, the aforementioned restriction exists as well.As I stated before, I will not argue. I'm only looking for the justification for this supposed implication.
@rdking Disagree with you, because if you say why private x should cause a.x to refer to private access, then why a.x can always cause to return undefined, but setter still will work:
let obj = {
get x() {
return undefined;
},
set x(value) {
...
}
};
And from user point of view it can seem like x has special behavior and it is !!
Also adding to name # break SOLID, Single responsibility
Name in such case serve two separate goal - access modifier and name of property ...
It is not extensible enough, because what if in future you will add another modifier, how it will look ... ???
Like this -#x or like this %#x ...
There is reason why other languages use private, protected ... because it shows intent directly that this field is private ...
Anyway we can argue about it, but actually I use such convention in Python like _ and __, but it has drawbacks that it is not obvious that this name has special meaning ...
@ljharb
think your questions are about “why would seeing this.x make someone think it’s private”
Um, no. My question is exactly as I stated it. It's about why "
private ximpliesthis.x", especially given that both a private member and a public member can be defined for the same instance object. Since it's already set in stone thatthis.xis public, and that will never change without breaking the language, why would any new notation suddenly suggest thatthis.xcan be non-public?
@rdking Acctually I do not understand why a.x access is backed into language that it always should be accessed ?
@ljharb
i don't think people think about public/private in that way.
So to be clear, it's just an assumption?
@redradist
Disagree with you, because if you say why private x should cause a.x to refer to private access, then why a.x can always cause to return undefined, but setter still will work:
2 things:
- I never said that
private xshould causea.xto refer to private access. I did, however, state that this is an implicit assumption in the presumed implication. I think it's rather off-putting of TC39 to have such an assumption buried in their justifications. - I don't understand what a pair of accessor functions have to do with whether or not
a.xis private.
Acctually I do not understand why a.x access is backed into language that it always should be accessed ?
If you're asking why a.x must always be public, the short answer is that it doesn't have to be. If TC39 had not adopted the stance of wanting full encapsulation for private fields, then by simply disallowing the existence of a public field with the same name as a declared private field for any given object (as is the case in most languages supporting class), it would be possible to allow a.x to access the only x available on a.
That's not what they chose, and their reasons are sound enough. I don't necessarily agree with the notion of weakening a language to protect against sloppy or bad programmers, but I also cannot find fault in their reasoning. Since TC39 chose full encapsulation, regardless of what the private field definition token looks like, there's no way that the access expression can ever be a.x for a private field.
@rdking anything that's based on people's possible mental models is an assumption. Assumptions are fine, anything that involves human thought can't truly be objective.
@ljharb
IMO, assumptions have their use. Justification is not among them. This is even more true when designing a very popular, widely used language. But maybe that's just me.
What if #foo was kept as "hard private" like it already is, and then a new private foo was a softer alternative introduced later (where this.foo can only access one particular instance variable with a certain access level)? Would that be worth having in addition?
Why? The word "private" is wildly inaccurate if it's not "hard private".
@trusktr Unfortunately, that would introduce confusion in the extreme. To make something like that work, a class wouldn't be allowed to have both private x and #y. You'd be forced to use either one or the other. It's not that it's not possible to do both in the same class, but that invites confusion. There's also the problem that if private is used, it would need to be impossible to add new members to an instance that have the same name as a private member. In short, while it could be done, it just doesn't seem like a good idea to have both notations.
@ljharb
Why? The word "private" is wildly inaccurate if it's not "hard private".
Um... your absolutism is showing. Think about it like this. If I have a letter in my hand, and I successfully keep you from snatching it away, the contents of that letter are private to me. Who cares if you can see it in my hand. This is what private means to most developers. The only ones doing something screwy that violates your idea of private are those who are doing non-language related things like trying to get around sloppy developers who don't maintain proper versions on their shared code. That's not an issue for the language, but for the shared code developers. While I understand that it's still a pretty big issue, I still can't say that it's within the domain of the language itself to fix (unless the language dictates a library format).
Why? The word "private" is wildly inaccurate if it's not "hard private".
@ljharb It does not matter that you do not like "private" keyword or it has inaccurate meaning ... TypeScript already use it, this keyword has been widely used in ecosystem and adopted by other languages ... For me those "+"-es more reasonable than it may have inaccurate meaning, because someone think such ...
If you will introduce new private field variable it will split ecosystem again ... You are trying to design language but it is already designed by TypeScript team mostly in good direction ... Just standardize TypeScript (without "namespaces") ;)
Also even if you will implement private fields like in this proposal how to implement than protected modifier ?
Like this _#x, -#x ... ?
Instead of thinking about some minor features like private fields, it would be better if TC39 improved current language by removing at least from language implicit type coercion ...
To make something like that work, a class wouldn't be allowed to have both private x and #y
@rdking Did you mean #x there? I don't see why both aren't allowed (they are both allowed in TypeScript, and it would carry over well, for example). Here's a simple example:
class Foo {
#x = 123
private x = 456
// x = 789 // this would be a runtime error, private x already declared.
test() {
console.log(this.#x) // 123
console.log(this.x) // 456
}
}
const f = new Foo
// f.#x // this would be syntax error like it already is.
// f.x // would be a soft-privacy runtime error, "x is private in Foo"
// f.x = 987 // would be a soft-privacy runtime error, "x is private in Foo"
class Bar extends Foo {
// private x = 123 // would be a runtime error, private x is already defined in Foo
}
Other OO languages have soft privacy; there is precedent for it.
Why? The word "private" is wildly inaccurate if it's not "hard private".
@ljharb Saying that we must have only hard privacy is merely an opinion.
Why don't we have both options and let developers choose, and make their own opinions?
Or even better: why not take the opinions of non TC39 developers into higher consideration based on the high number of ignored votes they've placed?
In short, while it could be done, it just doesn't seem like a good idea to have both notations.
@rdking Yeah, ideally we would have only one thing and it would be better than the current, but I think it might be nice to have another option (TypeScript already has both) if we can't reverse the current state.
If the private x version existed too (and even if it behaved like a normal prototype property except with restrictions on where it is accessed), I would find that to be very awesome and very usable and would prefer it over #private fields any day.
Furthermore, if I really really wanted people to see a property's usage site and immediately know the property to be private, I could easily adopt a double-underscore-prefix convention for that. F.e.
class Foo {
private __foo = 123
method() { console.log(this.__foo) }
}
(I already use this convention in all of my code!)
@rdking something being "absolutism" doesn't invalidate it, unless you're absolutely against absolutism :-p "I successfully keep you from snatching it away" would require also denying me any means of reflection, which is indeed what "hard private" means.
@redradist please read through all the closed issues in this repo, in which "protected" is discussed in great detail.
@trusktr we don't operate on the basis of votes; every opinion voiced here has been considered, as far as I can determine.
Why don't we have both options and let developers choose, and make their own opinions?
Great question! That's already possible. Developers can, and already do, use a convention (leading underscore), or Symbols, or documentation, to mark things as "please don't touch". What's missing is the capability to have "hard private" for instances without jumping through WeakMap/WeakSet hoops, which is what this proposal provides. For "soft private", I'm not sure a compelling case can be made that special syntax (something that has a high cost to add) should be added that can do a better job, or be easier to write, than _foo() {} or [Symbol.for('foo')]() {} or similar. However, "hard private" is a new low-level capability, which is often sufficient to warrant adding new syntax.
@ljharb
"I successfully keep you from snatching it away" would require also denying me any means of reflection, which is indeed what "hard private" means.
On this, we agree, almost. I think it would be better to say that "I successfully keep you from snatching it away" only requires an impervious defense against any attempt to access. On this point we, need to be clear. Reflection is the technique by which the structure of a thing can be revealed. However, there's nothing specific to the concept of reflection that allows you to access what has been revealed. Conflating revelation and access is problematic to say the least.
What's missing is the capability to have "hard private" for instances without jumping through WeakMap/WeakSet hoops, which is what this proposal provides.
You missed a few things. Also missing are:
- the ability to extend privacy via the prototype structure (i.e. protected)
- the ability to encapsulate soft privacy.
One of the many things that have bugged me throughout this process has been the mischaracterization of the concept of "soft private". If "hard private" means to be both hidden and encapsulated, then shouldn't "soft private" at minimum mean encapsulated"? If so, then Symbol and approaches like the _ convention are not "soft private" at all. These things are simply public as they always point to a publicly accessible property. So to say something like:
That's already possible. Developers can, and already do, use a convention (leading underscore), or Symbols, or documentation, to mark things as "please don't touch".
while trying to characterize "soft privacy" seems to miss the mark by quite some distance. If it's not encapsulated, then it's not "private" at all.
@trusktr
@rdking Did you mean #x there? I don't see why both aren't allowed (they are both allowed in TypeScript, and it would carry over well, for example). Here's a simple example:
It's just personal opinion, but for me, having them both accessible in the same class just invites potential confusion and errors. Having both "hard" and "soft" privacy in the same language would be good as it invites flexibility. Allowing both to be expressed in the same class feels like a recipe for disaster. My thinking is that for the sake of sanity, usage of "hard" or "soft" privacy on a per-class basis should be consistent. But that's just my 2 cents.
Given that, unlike many languages, in JS, a subclass can be created dynamically and at any time, anything "protected" is indistinguishable from "public".
Guys, why private in JavaScript could not mean hard private ?
I do not think it is needed to have "hard" and "soft" private ... there are lots of confusion ...
@ljharb
Given that, unlike many languages, in JS, a subclass can be created dynamically and at any time, anything "protected" is indistinguishable from "public".
You paint with such broad strokes! What you're failing to understand is that it is not an issue of security as is the case for "private". Sure, someone could dynamically create a subclass and gain access to the "protected" members. But so what? That doesn't make them public. It only makes them accessible from within a given scope that just happens to be broader than the scope for private, but more restricted than the scope for public... which is exactly what it's supposed to be. No surprise there.
When all is said and done, I don't care about "protected" that much. I've got bigger issues with how "private" is going to be implemented. I won't bother re-hashing them since that's just beating a dead horse. I'll just say this...
Where this proposal is concerned, all we really need are 2 things:
- encapsulation that doesn't break any other feature of the language when used together, and
- the ability to choose whether the former is "hard" or "soft".
@redradist
Guys, why private in JavaScript could not mean hard private ?
There's no reason it can't. It all goes back to the FAQ and that poorly based assumption I addressed here.
“#” so bad
Having both "hard" and "soft" privacy in the same language would be good as it invites flexibility. Allowing both to be expressed in the same
classfeels like a recipe for disaster. My thinking is that for the sake of sanity, usage of "hard" or "soft" privacy on a per-classbasis should be consistent.
That would be totally fair allowing a class to have only one form; it would give users of the language a better set of options.
Dear Santa, please give me protected and private for one of these holidays.
@redradist #100 @trusktr https://github.com/tc39/proposal-private-fields/issues/33#issuecomment-232457083 @ljharb https://github.com/tc39/proposal-private-fields/issues/33#issuecomment-309202710
@glen-84 Reading through those posts just reminded me of the likely reason we're in this debacle in the first place. Some library authors of popular packages want to be able to lock developers out of the internals so that there's less risk of breaking downstream packages when updating the internal logic of the libraries. It just hit me again how silly this argument is, and how simple the solution can be without getting us into the mess private fields is bringing.
- Library authors should recognize 2 things when developers hack into internals: a. Yes it's a pain, but it likely happened due to the potential need for new API in the library. b. They already likely know they did something ill advised. In this case, go ahead and break them. Sure they're going to be irritated (as well as their users), but as long as you've been properly versioning your library, and you provide an API for properly getting at what they needed, your library will only become more powerful as a result.
- API changes break downstream code all the time. If you're not properly versioning your library, that's not the fault of the language or downstream developers if you get complaints or find that some popular downstream package has hooked in somehow just to detect a version revision.
- Programming languages are suppose to be usage agnostic. As long as it's legal for the language, it's not supposed to matter whether you're writing a program or a library. Sure, it's in a programmer's nature to use whatever is accessible to accomplish their goals. However, being too aggressive in trying to block some of those possibilities on a language level only serves to cripple some portion of the language.
It's a shame that these 3 points do not seem to coincide with the understanding being applied to this proposal.