-
Notifications
You must be signed in to change notification settings - Fork 113
Revival of the "protected" request #122
Comments
I have read recently about a |
|
I agree that some form of But I think the syntax and semantics of protected and other access modifiers are still worth discussing now - it's always good to keep an eye toward the future. |
@mbrowne sums it up well: this is a promising area for investigation, but it's most realistic to consider it as a follow-on proposal. Would someone be interested in creating another repository to discuss the idea? In this repository, I would be most interested in discussing any reasons why this more minimal proposal might make it difficult or impossible to add protected as a future proposal. |
I was initially concerned about the lack of reflection capabilities in the current proposal, and I still have some concerns about calling hard-private fields just "private", since they work differently than "private" in other languages and will probably be overused by some developers, at least at first. (Although IMO that would often be better than underusing them and having no encapsulation...and the terse syntax But I do think we have a good path forward to address these concerns. For example we could have:
And maybe also the ability to enable reflection on all fields in a class with a single decorator:
|
@littledan it seems that there is a concern about syntax of class MyClass {
foo;
bar;
#someImplementationDetails;
} It doesn't seem to be a good choice if there will be more access modifiers, like class MyClass {
foo;
bar;
#someImplementationDetails;
protected someProtectedFiled;
} It seems weird that access modifier in one case is word and in other case is just leading sign. Obviously, we could try to use leading signs for all access modifiers, like this: class MyClass {
foo;
bar;
#someImplementationDetails;
^someProtectedField;
} But this is counterintuitive and complex. Newcomers will have to also learn all kinds of signs that is used to set access level for field. In my opinion it's better to adopt well-known syntax from other languages: class MyClass {
foo;
bar;
private someImplementationDetails;
protected someProtectedField;
} Also this kind of syntax is already adopted by huge part of ES community that use TypeScript (see docs). So this approach could be applied with only Additional note about motivation and
|
@Igmat Have you seen the private syntax FAQ? |
@mbrowne and I don't see any problems with declaring as |
@Igmat unfortunately, if one person doesn't see problems, and another does, then there's still problems with it overall. Many of us find that asymmetry very problematic. |
Indeed, given that |
@ljharb but problem still presents. |
@Igmat i think it's a very large presumption that we'd ever add another access modifier, since JS doesn't have "access levels" - it just has "visible/reachable" or "unobservable/unreachable". |
And what about motivation for having |
@Igmat anything that's reachable is public; anything that's not is private - there's only one kind of interface users see. |
@ljharb what are the boundaries of |
It was your words @ljharb. And I'm not the only one who see problems with |
"reachable" means "you can write javascript that can access it and/or observe its presence" (barring Function.prototype.toString, because otherwise the discussion is meaningless). I'm not saying nobody "needs" or wants other access levels, I'm saying JavaScript inherently does not have any others, and so no such feature would likely make it through the standards process. |
@ljharb I'd prefer that you not take such a decisive tone in responding to these comments, e.g., in #122 (comment). One purpose of discussing TC39 issues on GitHub is to ensure that the community has a place to give feedback to the committee, let us understand what they're saying, and then be able to draw a conclusion with the benefit of those points of view. |
@ljharb I was just about to comment on this...
To echo what @littledan said, this seems like an overly strong statement. Obviously you have the first-hand experience of being on the committee and I don't, but your statement implies that it would likely never happen. There's a big difference between something not happening soon, and it never happening. I hope the committee is more open-minded than that, even if it ultimately decides not to add native support for any other access modifiers. In addition, there are multiple ways that access levels in between hard private and public could be supported, both with and without native support. In other threads we have discussed how decorators could be used for this, e.g. |
@Igmat One other consideration is that private properties and methods can already be shared among the same module without being made public simply by not exporting them. That doesn't cover all the use cases for |
Keep in mind that many people are resistant to bringing a class-system into Javascript, most of them preferring C-like API constructs. And also many absolutely don't like inheritance at all, which "protected" is for. |
Ok, what about next compile from class A {
protected foo = 'bar';
}
class B extends A {
protected bar = 'baz';
baz() {
console.log(protected.foo); //prints bar
}
} Result: const protectedSymbol = Symbol('not shared symbol, probably hidden in some scope');
var A = (function () {
const protectedVars = new WeakMap();
function A() {
protectedVars.set(this, { foo: 'bar'});
}
A[protectedSymbol] = function (thisArg, property) {
return protectedVars.get(thisArg)[property];
}
return A;
}());
var B = (function (_super) {
__extends(B, _super);
const protectedVars = new WeakMap();
function B() {
var _this = _super !== null && _super.apply(this, arguments) || this;
protectedVars.set(_this, { bar: 'baz'});
return _this;
}
B[protectedSymbol] = function (thisArg, property) {
const ownProtected = protectedVars.get(thisArg);
return ownProtected.hasOwnProperty(property)
? ownProtected[property]
: _super[protectedSymbol] && _super[protectedSymbol](thisArg, property);
}
B.prototype.baz = function () {
console.log(B[protectedSymbol](this, 'foo'));
}
return B;
}(A)); Obviously it's not a bullet proof and full example (e.g. prototype/instance methods should be covered in a little bit another way), but rather Proof-of-Concept, that something like More of that - it doesn't use something very heavy - core principal is very close to Obviously, it's unlikely to find another addition to Also, TS is huge part of ES community - and Why not to use already-existing ES keywords ( |
Please see the FAQ. |
@bakkot do you think I can't read? Or it's just auto-reply when somebody suggests to use existing keywords, because argumentation in FAQ has mostly nothing about why I realize that this work is very hard - but I haven't seen ANY real problems for implementing it with keywords, only refering to FAQ which has not enough argumentation. Will it help if I create |
@Igmat The section in the FAQ to which I linked is titled "Why aren't declarations
That's true. That's been proposed a few times, but it seems like very few people want to use a keyword for access (especially if they couldn't have
No, probably not. I agree it's possible to implement with some semantics. That's not the issue. |
@mbrowne thanks, and I already know that and even more - use it in my own libraries. |
@Igmat From my understanding, it's all about "hard private" (the sigil) vs "soft private" (the "private" keyword). The first ensures that the private keys are not programmatically discoverable, while the latest can't. While we can analyze source code to get the privates keys and plan our attacks, the community has opted for hard private. |
@doodadjs Ignoring TS's @bakkot Though I don't ever expect it to happen, I would really like to sit down with you some day and discuss with you why the opinions which you espoused in your FAQ are not the end-all and be-all of whether or not we should use
I understand this and agree... to a point. It's not the first oddity of that type in ES and it likely won't be the last either. There are similar inconsistencies around the primitive wrappers when compared with Symbol (which doesn't allow
So to you and others, I'm going to suggest that you please quit relying on the FAQ. Those who are making this argument to you have already read it and don't agree that the opinion you expressed is in the best interest of the language and its users. @ljharb Please quit harping on ES not having access levels. That's like saying binary doesn't support the digit 2. While very much true, that doesn't mean the functionality can't be supported (2(base 10) = 10(base 2)). I used binary because this is very apt for what's being requested. What we're asking for can easily be implemented. If we consider var obj = {
priv: {
priv: {/*private data*/},
pub: {/*protected data*/}
},
pub: {/*public data*/}
} and inheritance looks like var obj2 = {
priv: {
priv: {/*private data*/},
pub: {/*protected data*/, __proto__: obj.priv.pub}
},
pub: {/*public data*/, __proto__: obj.pub}
} Side Note: |
@rdking i bring up the point about access levels because people keep talking about |
@ljharb I somewhat understand your discomfort. Doing this non-publicly is simply not possible given the current proposal without resorting to the implementation of a pattern that is likely less than straight forward for most users. It's not something I would put in a node module and just expect people to understand how to use. If the results of that pattern can be hidden behind a keyword like |
@Igmat that's a node implementation detail, and not part of the language. Separately, as long as your decorated class runs first, then it'd be impossible to hijack that later - in JavaScript in general, if your code doesn't run first, there's a number of things you can't defend against, but that doesn't apply to modules. |
This might have been proposed already, but I had an idea: what if the syntax were:
At first this seems redundant, since
I think the biggest downside of my proposal is the question of how you handle this:
It would either have to be a syntax error, which I'm sure would be confusing to people, or do something special other than hard private -- which might be even more confusing. So I'm not sure if this is an improvement on this current proposal, but I do think it has a big advantage in terms of future extensibility to other access modifiers. |
See tc39/proposal-private-fields#53. Though there might be other reasons to revive that syntax - in particular if we wanted to allow declarations to occur outside of classes.
It would absolutely be a syntax error. |
|
What does “well defined” mean to you? In either case it would be precisely and identically “well defined” in the grammar. |
@dalexander01 your last comment on tc39/proposal-private-fields#53 was from 2016, so I'm curious to know: are you still a proponent of the |
With the understand that programming is little more than a variation of applied math:
In the case of a programming language, "unique interpretation" is more apt than "unique value". The problem I've been having with this Nothing else in ES suffers this lack of "unique interpretation". Within a given context, every token can be uniquely placed in one of the following categories:
Any arguments contrary to this statement should be placed in #133. Not here. The simple fact that |
If one day ECMAScript will support other access modifiers ( As for what |
"Train up a child in the way he should go, And when he is old he will not depart from it." Prov. 22:6 Why did I quote the Bible? Simple. Even in ancient times, they knew that people stick with the patterns they've grown accustom to. It will do little to no good to "allow optional |
@nicolo-ribaudo The currently proposed syntax would establish in developers' minds that
...presumably because the inconsistency would be confusing. The reason I am changing my tune is that it's dawning on me that if this proposal moves forward as-is, native support for something like And regardless of that, I certainly think it's very unlikely that something like the following unprefixed example would ever gain community or committee support (and I myself would probably be against it if it meant reduced performance for all property lookups):
And beyond those two options, I don't see any possibility of sharing private fields except for workarounds using decorators. In the case of reflection (e.g. soft private) I think that would be fine, but I think it would be really suboptimal for protected/internal/friend/etc. |
Just in case my fears are unfounded, does anyone think the following syntax would be realistic and viable at some point in the future:
(Feel free to substitute I find the inconsistency problematic, but maybe the committee disagrees. If we don't see the above syntax as viable but still don't want to change it to |
I don't find it too bad: It's the same I consistency that we would have without class Foo {
x = 2; // <-- no keyword
private y = 3;
protected z = 4;
} |
I think the inconsistency is a bit jarring. In #136, I finally figured out how to point out the core inconsistency in this proposal that makes |
As discussed upthread, we can consider |
@mbrowne yes, I still am a propoent of the private #x syntax for all the reasons indicated in the previous issue. |
Most of the people I've talked with about |
Thanks for the explanation, @littledan. I suspected this might be the objection to the syntax, but wasn’t sure even after reading all the comments on the old thread why the issue was closed without really explaining why, so it’s good to be clear on why this was rejected. Were the people you talked to who thought it was redundant aware of its implications for the possible addition of other access modifiers in the future? I ask because I would have had the same initial reaction (and for a long time didn’t have any concerns about the absence of a Having said that, I can understand a general aversion to avoidable verbosity and I can also imagine some people finding this confusing. So I’m not going to raise a strong objection here, but I will say one more time that I hope the committee has thought about more access levels other than hard private and public and will be open to the idea of adding native support for these in the future if decorator solutions turn out to be unsatisfactory. So that means being open to the syntax that I think was summed up well by @nicolo-ribaudo as being not “too bad” - so not good, but perhaps acceptable. But among so many objections from other people from the community participating in discussions here, I’d like to end on a positive note. I very much look forward to the availability of private fields, and despite my concerns I think the currently proposed syntax and semantics will still be very usable. More transparency on the goals and hard-line requirements for the proposal would have been really helpful and hopefully you can still offer more of that for questions still unanswered. Nonetheless (as you are probably aware), I don’t think the overwhelmingly negative response here is representative of how the community as a whole will respond when this is actually released, at least not after people read why the syntax choice makes sense and get over any initial aesthetic reaction. Clearly there’s no way to make everyone happy here and some people will always feel that a very wrong decision was made, but IMO the committee should be commended for a huge amount of work and patience over a period of years on this proposal, and engaging with the community in a very open and public forum. |
Mostly I agree
In theory we always can postpone it to follow-on proposal, but we should notice current proposal leave a very limited design space of syntax for Because this proposal already chose sigil, it will be very strange and inconsistent to introduce keyword |
Technically speaking, I agree with you, this proposal is "usable". But consider already have many other privacy solution in the wild, an "usable" but not convinced proposal will just add another sideway to the divergence. It's harmful to the community. Your follow assumption (about how community will respond) is too optimistic. Copy from my other comment:
@littledan This is the essential reason I am against this proposal even without consider the existence of a better alternative like classes 1.1 . |
It seems that symmetry is the main reason for the https://gist.github.com/trusktr/3088a7a301d96f099e2a0d1aed1403a2 Honestly I don't think it will be hard to learn to use the keywords symmetrically. |
@bakkot That's a nice goal. That said, JS doesn't have to strictly be a follower and fit in with other languages. It can, for example, have Learning types in TypeScript, for example, is a much bigger endeavor than learning to use access keywords symmetrically as in the above example (ignore semantics, rules, and functionality, I'm only referring to using the keywords symmetrically). |
@trusktr Given that you're a proponent of |
Seems that the same thing applies for switching from public to private, but yeah, that's not a good reason not to have private with a sigil. The same applies with
It may be more verbose, but if it were implemented in some sort of way like with "access modes", then it'd be interoperable with all the patterns we use today on normal objects, and all the libs like underscore, lodash, etc. I'm just trying to think about making it work well with what we already have. Private fields is a another thing in an of itself which are not properties, and do not work with existing tools, not even the built-in ones like |
#100 (comment) has more upvotes than downvotes, because those people that upvoted want the same sort of dynamicism which the current private fields do not offer (if that's not it, then they just like the verbosity alone). Personally, I think offered functionality of a feature is much more important than the amount of keystrokes we save with the feature. I really don't mind typing "private" if I get all the extra dynamicism that @bdistin enumerated in the table. |
I've had prior discussions with @ljharb over this issue. The gist of his argument is that
protected
provides no actual protection from access in any way that significantly matches the capabilities of the same keyword in other languages. As a soft-private feature, it doesn't fit the goals for the proposal.This is where we disagree, tremendously, and for 2 very good reasons.
protected
can have the same effect in ES as it does in other languages supporting the concept.protected
never provided anything like security in other supporting languages.The argument against having
protected
has often been like this:class
can be subclassed at any time.class
instance will have access to theprotected
members of any other instance.protected
access is still public.This argument is very much correct. This cannot be argued against. However, it's just a strawman argument. It misses the point entirely. This kind of argument presumes that the point of
protected
is to actually "protect" something. This is not true. In every language that uses it,protected
is a tool for separating the API space of an object into 2 separate domains: 1 for members, and 1 for non-members. Even in a statically compiled language, a developer can create aclass
with methods that leak theprotected
members of its base. ES, being a scripted language, just makes this leak ability a little less time consuming.This fact is very much irrelevant to the purpose of
protected
. Whereprivate
has always meant that the declared item is an internal implementation detail that cannot be seen,protected
has always mean that the declared item is an internal implementation detail that is shared with extending objects but not with unrelated objects. The only cost of entry is the ability to extend the original.Hopefully, that clears things up around what
protected
is supposed to mean. Now, why do we need it? Presently, everything is either public, or through great pains (WeakMap & closures) hidden. Even before the debut of theclass
keyword as a functional part of the language, developers were creating object factories that mimicked the behavior of classical OOP hierarchies.Unfortunately, there was no simple approach to providing any semblance of
protected
support. So what developers began to do is adopt the '' convention where any member whose name begins with an '' is treated as an implementation detail. The developers would, of course, use these so-called implementation details in their own subclasses, but had no way of guaranteeing that other developers would play by the same rules. Theprotected
capability is what can provide that functionality. That is indeed the original intent.If a
protected
member is treated as a "public member of theprivate
interface", then any object or method with access to theprivate
interface will have access to theprotected
members. The benefit of doing this is an increase in the clarity of the design of all object hierarchies using this feature. Library developers will be able to create clear and clean external and internal APIs while protecting all of the implementation details inside the private interface. This will also increase the readability of any code using such libraries as it will be clear from the deliberate use of inheritance whether or not various members were intended to be publicly exposed. This will in general lead to an overall improvement in the quality of all adopting code.I am not proposing here that
protected
support be added to this proposal. What I am asking for instead is that this proposal be reworked such thatprotected
support becomes a natural extension to be added at a future time. As it stands, lackingprotected
support means that developers will still continue to use the_
convention as there will still be no means of hiding the shared internal API of their libraries from the publicly exported interface.The text was updated successfully, but these errors were encountered: