-
Notifications
You must be signed in to change notification settings - Fork 113
[Suggestion] Consider to use time proof solution with private and protected keywords #315
Comments
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
|
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. |
Um, no. My question is exactly as I stated it. It's about why " |
@rdking because i don't think people think about public/private in that way. I think if they see |
@rdking Disagree with you, because if you say why let obj = {
get x() {
return undefined;
},
set x(value) {
...
}
}; And from user point of view it can seem like Also adding to name # break SOLID, Single responsibility 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 |
@rdking Acctually I do not understand why |
So to be clear, it's just an assumption? |
2 things:
If you're asking why 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 |
@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. |
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 |
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
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 |
@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 ? 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 ... |
@rdking Did you mean 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.
@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? |
@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 |
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.
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 |
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.
You missed a few things. Also missing are:
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
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. |
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 |
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 |
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:
|
There's no reason it can't. It all goes back to the FAQ and that poorly based assumption I addressed here. |
“#” so bad |
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 |
@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.
It's a shame that these 3 points do not seem to coincide with the understanding being applied to this proposal. |
@ljharb Knowing that you disagree with my post, I'd like to know why, either in a different thread or on discourse. Understanding why anyone in TC39 can't agree with this kind of understanding is critical to understanding the mindset of those who would champion or even advocate new proposals. |
Why not give most of us the less-hard privacy with the Supporters of this proposal (a minority) tell the majority of people to go out of their way to use features like A flip of priorities seems like the logical solution: opposite of the current proposal, and based on community feedback and not a result dictated by a mere handful of people. Or better yet, give us both, as described above, so that the majority of people can have a decent option, while those few can still use the current proposal's features if they really need to. |
I've been thinking about the whole "protected" thing and realized that actually there is kind've a way for protected to work that isn't accidentally public. I realized this because this pattern is somewhat already present with the class SubPromise extends Promise {
#resolve;
#reject;
constructor() {
// Resolve and reject are essentially protected state, instances are given
// copies and can do what they want with them (including making them public)
// however even with this I can't capture resolve/reject from an arbitrary instance
super((resolve, reject) => {
this.#resolve = resolve;
this.#reject = reject;
});
}
} The main problem with this style of initializer pattern is it can be hard to add to existing APIs after the fact. Something like protected would help alleviate these cases where you want to expose a bunch of stuff to the subclass, but don't have anywhere to put it (e.g. due to using variadic args to constructor). For example: class SomeClass {
protected #newHook;
#data;
constructor(...data) {
this.#data = data;
}
}
class SomeSubClass {
constructor() {
super();
// Because it's on super not this, there is no way inside SomeSubClass we can access
// protected properties of an instance of another subclass of SomeClass
super.#newHook();
}
} |
@Jamesernator the pattern of passing hooks up through super is viable, but we don’t need any new features for that - just a convention. Your second example doesn’t really work because a subclass can be created at any time, both of SomeClass and also of SomeSubClass. |
Other subclasses of Also I actually realized passing hooks like in my class SubPromise {
#resolve;
#reject;
constructor() {
super((resolve ,reject) => {
// Oops #resolve isn't created yet as super() isn't finished, so this throws a TypeError
this.#resolve = resolve;
});
}
} The real pattern would have to be: class SubPromise {
#resolve;
#reject;
constructor() {
let resolve_;
let reject_;
super((resolve, reject) => {
resolve_ = resolve;
reject_ = reject;
});
this.#resolve = resolve_;
this.#reject = reject_;
}
} This is a lot more awkward and error prone to deal with than the strawman |
@Jamesernator If you're looking for a way to do protected state that isn't accidentally public, not only is it possible, I showed and early prototype of it to @ljharb, and through his feedback actually worked out how to solve the direct issues he pointed out. I've been using the results of that in my own projects since then. It's a small solution but not exactly simple. A full example of the logic for this is here. This approach doesn't involve passing anything through the constructor. Further, if it were implemented in-engine some of the complexity could be reduced due to having access to internals. Note: At the time of that discussion, despite the functional logic, @ljharb still maintained a strong stance again inclusion of |
it looks # is used for this record and tuple https://github.com/tc39/proposal-record-tuple class Something {
#test1 = #[1, 2, 3]
#test2 = #{ hello: 'kitty' }
} so this look not so good |
That’s an argument against Records and Tuples, not against already-shipping stage 3 class fields syntax. |
The "No peeking at your cousin's privates!" log statement in the catch block. LOL |
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
andprotected
keywords ;)The text was updated successfully, but these errors were encountered: