-
Notifications
You must be signed in to change notification settings - Fork 113
Non-debate thread: explaining the reasons for this proposal's decisions #178
Comments
I forgot to mention...for those who haven't checked the readme for this repo in a while, more details on committee decisions can be found in the links provided under "Development History": So this thread is for clarifying and asking follow-up questions, not for revisiting the whole decision process from the beginning. |
The argument against private symbols eventually became, once you have all the "right" semantics, it is so far away from ordinary properties that it's not reasonable to make them treated as property keys with [] anymore. That's why decorators makes PrivateName not a Symbol/property key, even though it serves the same role. |
Apologies for my delay here and thanks for following up like this! |
@littledan what are the "right" semantics? |
Thanks @littledan. Can you comment on the committee's overall views regarding alternative solutions for hard private? For example, if private symbols didn't have these issues and were seen as a fully workable proposal, would the committee have been likely to consider making private fields soft private instead of hard private? Or would it have maintained its preference for hard private class fields? I'm asking these hypothetical questions as a means to an end: I'm trying to get at the driving forces behind the total rejection of FWIW I see both pros and cons of fields being hard private by default, and even without knowing all the details the committee's decision on this seems very reasonable to me. So I'm not asking for a primer on the benefits of strong encapsulation by default, but rather clarity about which of my points in the OP were most salient to the committee and whether I missed anything that was a significant factor in the decision. |
I don't think so. You can read some of the discussion here, if you like, but my position and, as I understand it, the position of several other members of the committee is that "soft" private is not meaningfully distinct from symbols as they currently stand, so if we're adding a new thing for private state, it should actually provide privacy. As to which should be the default, it is my position that it should be the case that the default behavior for a new feature is the new capability it provides, not the thing you could do already. (That said, private symbols would be "hard" private, as I use the term, unless they triggered proxy traps.)
The FAQ already summarizes my position on this to the best of my ability. The short version:
None of these points really depend on the hard vs soft thing. Of course, other committee members may have their own opinions. This is an inevitable part of the consensus process - even if everyone agrees on a design, they won't necessarily (or typically) agree on the reasons for it. So I'm afraid you're unlikely to get a statement from the committee as a whole on this. (Edit: forgot one more point: It is unacceptable for the |
My understanding of the committee's position matches @bakkot's, even if my personal feeling is that many of these things are tradeoffs rather than unacceptable. |
This is a good suggestion. Can anyone pick up to summarize considerations behind |
Thanks @panlina...I was intending to follow up on this thread, just hadn't gotten to it yet. First of all, thanks very much to @bakkot and @littledan for the explanations. I think we have the beginnings of a rationale document here. I think there is also another point in favor of hard privacy in addition to what you mentioned, which is that soft privacy (without making fields fully public) is still easily available via decorators. Although...it's not actually quite as easy as I originally thought it would be, since only field-level decorators (and not class-level decorators) have access to private fields in the latest decorators spec, but that's a discussion for the decorators repo. |
Regarding [[Define]] vs. [[Set]], I agree that's a very good choice for a next topic to discuss here. I still don't really understand the case for Define and strongly favor Set (for practical reasons), but I'll take a stab at summarizing the argument to get the process started... As best I can tell, the main argument in favor of Define is avoiding side effects, meaning that declaring a public field should always do one and only one thing: define an own property of the instance. It seems that the distinction between declarative and imperative syntax is important here; field declarations are intended as declarative syntax, so side effects such as a getter or setter being called (which would be acceptable in an imperative setting) are undesirable. What I don't think has been demonstrated is that there are significant practical downsides to Set, or at least none that come close to the significance of the practical downsides of Define, i.e. potentially confusing issues for developers and resulting bugs. (Purely declarative semantics do have the practical benefit of helping to make code more readable and predictable in general ways, but I'm referring more to specific points of confusion or surprising behavior specific to Javascript and its established expectations.) I bring this up here because although this is a non-debate thread, I would definitely appreciate any committee members describing more practical issues with Set that I might have missed. One other issue with Set I've seen mentioned by committee members is that it could complicate the implementation of decorators. @ljharb offered an alternative interpretation, saying that Define also comes with its own challenges for implementing decorators, and it's more of a 6 in one half dozen in the other situation. @littledan, perhaps you could weigh in here as one of the champions of the decorators proposal (and also this proposal of course)? |
I followed up on my point about reflection here: |
#196 takes a first stab here. I merged it already, but review comments still welcome. |
Thanks @littledan. In addition to what you wrote in the readme, would you say that the points I mentioned above (declarative syntax, avoiding side effects, and possibly working better with decorators) were the main reasons for this decision? Also, I am wondering about @rdking's suggestion (which I think @ljharb said he had proposed as well) to use |
@mbrowne, I think the committee has previously discussed and rejected having both, for roughly the same reason it has discussed and rejected having syntax for both hard and soft private - it means another thing to think about every time you write a field, which will often not make much difference. (Here more than with hard vs soft private - set and define semantics will be actually identical a large majority of the time.) |
@mbrowne Yes, those are all important design considerations as well. |
I just wanted to note that I welcome involvement from anyone else who wants to work on further documenting the rationale for this proposal. I might return to this at some point, but I think the readme and FAQ are more complete now, and I no longer have many questions that are really perplexing to me personally--I mostly get the reasoning now even in the (few) areas of this proposal where I have doubts or disagree. Although I'm not going to personally pursue this at the moment, I would say a next area that could be better summarized/consolidated for posterity would be the reasons this proposal is considered superior to classes 1.1, class members, private symbols, etc. |
Feel free to mine content for this text from slide decks I wrote in response to those ideas in the TC39 meetings where they were/will be presented, linked from the agendas. |
There's something about this proposal and the surrounding discussions that I still don't understand, which is the attitude that native |
It's not about performance, but about the definition: how would we reliably distinguish "inside" from "outside" the class when subclasses are considered "inside"? I can't think of a strong way. |
The parent class has always been evaluated already at the time the subclass is being evaluated, right? Protected fields could work according to different rules than private ones. I'm under the impression that there's a practical barrier here but I'm not understanding what the real issue is, i.e. why does it matter that subclasses are syntactically "outside"...is the mutable [[Prototype]] slot part of the issue here? |
The problem is that you could make a subclass which reads the protected information, and then .call() this method on some other instance, so there is really no strong protection. |
So are you saying that it would be doable if it were not for the existence of |
No, you would still be able to do this: class A {
protected #x;
}
class B extends A {
constructor(secret) {
this.#x = secret;
}
}
class Thief extends A {
static steal() {
return this.#x
}
}
var secure = new B("secret");
secure.steal = Thief.steal;
var secret = secure.steal();
delete secure.steal; |
@nicolo-ribaudo Can't you do that in any of the existing languages with protected members? The way I see it, protected access is about preventing naiive or inadvertent access outside a class hierarchy; it's not meant to be a security feature. But I have a separate question, while you're here. Suppose someone didn't want to use TypeScript for whatever reason, but they wanted protected fields and methods. I'm just curious, would it be possible to write a Babel plugin for protected members without completely rearchitecting Babel? Obviously it would require more static analysis than the average Babel plugin... |
Symbols already prevent naive or inadvertent access outside a class hierarchy. |
Good point. Of course symbols make fields more verbose to declare. And they prevent the use of dot-notation, but I guess that's not a big deal. But it would certainly be easier to write a Babel plugin to convert a field name to a symbol than to actually enforce that a field is not being accessed from an unrelated class (or externally). And hopefully in the future we would be able to do that with a decorator too. |
I think the next step for this thread would be to make a PR (or a series of them) to add the relevant clarifications to the readme or FAQ. I am not sure what clarifications would be beneficial, since I think we already have pretty good coverage of these points, but I would welcome PRs to cover my blind spots here. We've covered all of these points at length in other threads; it's the time to consolidate the documentation now. |
@littledan I'm not aware of a thread where the technical challenges and potential problems with a native |
It depends on how much declarative they are. class A {
protected #x;
}
class B extends A {
inherit #x;
} |
@littledan If I can provide you with a strong means of implementing protected that does not risk leaking the scope to sibling classes, can consideration for I've provided this link many times before. If you take the time to review it, I'm sure you'll see that it provides some potential for the strong disambiguation you want. |
I think we should continue this discussion in a different thread. Admittedly my question about Babel doesn't really belong here either. |
The goal of this thread is to begin consolidating and documenting the motivations and considerations that led to the current proposal. I think this information will be very helpful for explaining the proposal to newcomers to help mitigate negative or perplexed reactions and move them more toward, "ah, that makes sense." I think it's also useful to document the reasoning behind decisions for posterity, so that anyone interested in deeply learning the language can understand how it evolved and why. (This includes why it was chosen over alternative proposals.)
** Please pay attention to the title of this thread: I would like to avoid debates here (just in this thread). I am very much in favor of healthy debates (and have participated in plenty here) but I think for the purpose of this thread they would be noisy and distracting. **
Let me start by summarizing the general considerations regarding hard privacy. From what I can gather from @ljharb's comments, first of all hard privacy is seen as a very important requirement because there are use cases that can't be achieved any other way. Some of those use cases, such as making it impossible to branch off of the existence of a private field, or avoiding collisions with public fields, are particularly important to many library authors. It also sounds like there are members of the committee who believe that "privacy" in other OOP languages doesn't go far enough and that hard private is simply a better default—a more correct way of ensuring encapsulation.
Is that a fair summary so far of the motivation for hard private fields? I think these are good and valid arguments, but I think the case would definitely be stronger if it can be established that alternative options for hard privacy such as private symbols are unacceptable—especially given how consequential this decision is.
So I would like to pick up from my comment in #175 (comment):
It seems there is an unresolved question related to this from @Igmat:
#175 (comment)
#149 (comment)
This proxy issue with private symbols seems like a key point...after all, even @ljharb said:
OTOH, it's moot if the committee's position is that soft private is simply a bad default even if there are other ways hard privacy could be achieved. I'm not clear on the committee's reasoning on this.
The text was updated successfully, but these errors were encountered: