Closed 1316346949 closed 5 years ago
What is your question?
What is your question? @nicolo-ribaudo Use private properties like this, instead of using # directly,Personally, this seems to be stricter than just using #。
Ok, this should answer your question: https://github.com/tc39/proposal-class-fields/blob/master/PRIVATE_SYNTAX_FAQ.md#why-arent-declarations-private-x
From the FAQ:
This sort of declaration is what other languages use (notably Java), and implies that access would be done with this.x. Assuming that isn't the case (see above), in JavaScript this would silently create or access a public field, rather than throwing an error. This is a major potential source of bugs or invisibly making public fields which were intended to be private.
There are (and have been from the beginning) issues with this argument.
private
is used in other languages. The problem is that this assumption is due to the structure of a class
in those other languages. None of them are prototype-based like ES, and none of them structure their class instances in the same way as ES. The very definition of a class instance in ES before class-fields is an object sharing the prototype of the target class. To maintain this "implication" requires changing what it means to be a class instance. Therefore, the assumptions that lead to the afore-mentioned implication don't apply. There's not a single ES developer who wouldn't understand that these differences lead to different usages.#
and what it means will cause greater confusion for others, especially those new to the syntax.#
as a non-name character meaning "private", the presence of 2 fields in the same class sharing the same name with different access constraints is another point of confusion that async
, & static
).There are more issues, but this is already getting long. My point is that while the FAQ does state the decisions made, it does next to nothing to reveal the reasons and requirements behind those decisions. As such, those who can't help but see the issues with those decisions will keep showing up wondering why the decisions are what they are and suggesting alternatives.
I think this is answered clearly by the FAQ, so closing.
@littledan In the interest of preventing future questions like this one, can you or one of the other TC39 members address in a somewhat thorough fashion the "Why?" questions behind the decisions detailed in the FAQ?
To be clear, I'm asking for an explanation of the considerations that allowed issues like the 4 I listed above to be dismissed considered an "acceptable trade-off". If developers could see the reasoning behind the decision, maybe we'd be less likely to question it.
The FAQ tries to address "Why?", but maybe it doesn't do a thorough enough job. PRs are welcome to make it more complete.
I would if I could, but I don't have that information. Only TC39 board members who were part of the meetings that discussed these issues would understand the reasoning behind the decision. What the FAQ gives us is a "What?" answer, namely what the decisions were, and a rather pithy justification in lieu of a "Why?" answer. Try re-reading https://github.com/tc39/proposal-class-fields/issues/247#issuecomment-502417870. There's nothing from the FAQ that addresses the issues that come along with the choice made, right? Many of us want to understand why the numerous issues with the numerous decisions that had to be made were viewed as acceptable trade-offs. No outsider like myself can ever provide that information.
@rdking The answer to "what about these other potential problems with this syntax" is generally either "we don't expect that to be an issue in practice" or "while we expect that to occasionally be an issue in practice, this design still seems best". None of the specific things in your comment above are frequently asked, nor was there to my recollection any noticeable disagreement about any of them in TC39, so they didn't get added to the FAQ. If it would help, we could add an item saying "what about other issue _?" with an answer "It's possible we didn't think about that, but in the event that we did: on balance this design still seems best to us; no design is without some costs".
I realize "this still seems best" probably isn't the sort of answer you're looking for. It seems like you're interested in a more philosophical "why", as in "why do you value things you value". I'm afraid I don't really know what an answer to that could look like - what do you imagine an answer to that question would look like?
@bakkot Thanks for chiming in. I don't suspect that there would be much in the way of disagreement when there's a "consensus". That's just tautology. And you're right. None of the things in that post are "frequently asked". However, I bet if you answered to those issues, you'd see less issues like this one.
Certainly there's some comparisons behind that "seems best". Exposing them and explaining why A > B && why B < A would do wonders for helping the community accept these experience interrupting decisions.
what do you imagine an answer to that question would look like?
First, understand the question. Consider this:
class
declaration. This is already something outside of all prior ES paradigms.class
is a public prototype property.Now let's add on public fields:
#
), causing it to become a private instance property.Notice the parallel in the wording? That is evidence that the #
is being used in place of a keyword. This is how people will see it. Those of us who would like to add more class features in the future see this out-of-character-for-the-language lack of a keyword to modify the placement of a property as an impediment. Address this directly.
Next, where the FAQ states that private
implies this.x
, explain why this implication is a valid consideration given that class
is already very different from its compiled counterparts. In a compiled language, something like this:
class Foo {
public x;
private y;
constructor() { x=2; y=3; }
}
is perfectly valid because public x
implies that x
in the lexical scope of the class is actually this.x
. The same is true for private y;
. However in the ES equivalent, that x
could only be accessed via this.x
, and with this proposal, that y
with this.#y
. No one is going to argue that something (be it the sigil or something else) needs to be there for private access given the known requirement of having a separate namespace for private members. However, it should be noted that this is yet another difference between ES classes and compiled classes since with compiled classes, all fields in a class share the same namespace.
For static members in compiled classes, they can also be accessed lexically, or via the class name, or in some languages, even via this
. The ES equivalent only gives you via the class name or via this.constructor.x
. There's already plenty of deviation away from the implications that are valid for compiled languages. Explain why the implication that private x
implies this.x
is valid in the face of the existing language.
If you or anyone in TC39 can meet those 2 requests, then you will have successfully answered the "Why?" question about the syntax.
Those of us who would like to add more class features in the future see this out-of-character-for-the-language lack of a keyword to modify the placement of a property as an impediment. Address this directly.
This isn't a question. Yes, I agree that this is potentially an impediment to such people, though I don't believe that it would be fatal. But on balance this design still seems best to us; no design is without some costs.
I am still not sure what you think an answer to "why" would look like.
Explain why the implication that
private x
impliesthis.x
is valid in the face of the existing language.
Because, based on my experience learning, teaching, using, and observing other people use this and other languages, I expect many users of the language will implicitly assume that to be the case, and because this.x
is already valid syntax the language will not immediately make it obvious that they're mistaken. Yes, you can try to argue that they're "wrong" to have this assumption (although I don't think that's a very useful frame). But, nevertheless, they will.
@bakkot For the first part, I'll reword that into a question.
Why is the current choice of syntax, which poses an impediment to future related development requests, and is inconsistent with existing syntax design, the best design on balance as opposed to a design which preserves the existing syntax pattern (easing the learning curve), and does not impose an impediment to future related development requests?
For the second part, this is almost good enough. The only thing that is missing is consideration for the fact that having private fields in a separate namespace is a requirement. All who raise the question over this issue understand that having a separate namespace for private automatically disqualifies this.x
notation. Therefore, as has been my experience, most programmers, upon hearing or reading this requirement, either stop assuming or never begin to assume that this.x
is the correct syntax. Did you not have a similar experience? Or is it that you did not make all the requirements clear to those you were teaching?
I'm trying to point out that the assumptions/expectations you're making have to be reasonable in the face of the known requirements. So your answer to the "Why?" question must not assume ignorance of the requirements. The one you gave above appears to assume ignorance. Can you try again?
I'll reword that into a question
The best answer I can give is "I do not believe there is a better alternative". The FAQ discusses some broad categories of possible alternatives and gives relatively concrete reasons one might not like each. I find those flaws to be more significant than whatever inconsistency there might be in this feature. I don't know what an answer to "why do I find those flaws more significant" would look like.
Did you not have a similar experience? Or is it that you did not make all the requirements clear to those you were teaching?
I do not assume that I personally will be teaching everyone who encounters this syntax, nor that anyone teaching it (myself included) will be sufficient proficient as a teacher that all of their pupils will instantly and permanently internalize the relevant design constraints and understand their consequences. But even if they did, the most important consideration in the design of a feature is not that users of the language understand why we chose some particular design, but that they be able to read and write with it confidently and with a minimum of errors.
So your answer to the "Why?" question must not assume ignorance of the requirements.
In general I do not believe the designers of a language should assume that all users of that language will have complete knowledge of the requirements considered important in the design of each feature. So I do not accept this claim.
Remember, these questions aren't a matter of your personal choice but rather probes into the rationale behind the board's final decisions. Also, it isn't a matter of "complete knowledge of the requirements", but rather of the relevant constraints. Language users need to know what they can and cannot do with a language. Knowing that you can have "private and public fields with the same name" is relevant knowledge that automatically excludes use of this.x
notation. Hence the invalidity of the previous implication.
That being said, it's becoming clearer that there doesn't appear to be any real answer to the "Why?" question here. Instead, it is as you said, "because we just think it's better despite the problems". While this may be as distasteful as it comes in technical design, it happens.
It doesn’t exclude it though - many many people on this very repo would assume that if x is private, accessing instance.x
from outside the class would throw - ie, they would assume that public and private fields shared the same namespace.
it's becoming clearer that there doesn't appear to be any real answer to the "Why?" question here
Again, I just don't know what you think such an answer could look like. Concretely, we have several possible designs, each with some costs and some benefits. Let's say you picked one. How would you answer why? What could it boil down to other than "in the light of these costs and benefits, given my experience with this and other languages, I expect this choice to have the best outcome"?
To be really clear here, I'm asking you not for a rephrasing of this question, but for an example of what your answer to a similar "why" would be.
@bakkot Try expressing your answer using these.
@rdking I can extract that from the FAQ for you, if you like:
We considered the current approach, changing the meaning of the expression this.x
in a class body which declares a private field named x
, always having obj.x
refer to a private field inside of a class which declares a private field x
, having declarations be of the form private x
and access be of the form this.#x
, [etc].
We chose the current approach because it seems the design which would best lead to people being able to confidently write programs with a minimum of errors.
The possibility of changing the meaning of the expression this.x
in a class body which declares a private field named x
was considered undesirable because this
is already a source of enough confusion in JS; we'd prefer not to make it worse. Also, it's a major refactoring hazard: it would be surprising if this.x
had different semantics from const thiz = this; thiz.x
.
The possibility of always having obj.x
refer to a private field inside of a class which declares a private field x
was considered undesirable because class methods often manipulate objects which are not instances of the class and it would be surprising if the code obj.x
suddenly stopped referring to public field x
of obj
, when obj
is not expected to be an instance of the class, just because that code happened to occur somewhere within a class which declares a private field named x
, possibly deep within said class.
The possibility of having declarations be of the form private x
and access be of the form this.#x
was considered undesirable because declarations similar to private x
are what other languages use (notably Java), and imply that access would be done with this.x
. Assuming that isn't the case (see other FAQ questions), in JavaScript this would silently create or access a public field, rather than throwing an error. This is a major potential source of bugs or invisibly making public fields which were intended to be private.
Not all of that is in the FAQ. Third approach certainly is, but not the first 2. There's only 1 issue with the 3rd approach: don't use "imply". Like I've said, the implication to which you refer is not valid to those who understand ES. Only those who are new to the language, or don't understand it well enough, or aren't aware that private fields have a separate namespace would be likely to make that implication. ES already doesn't follow the access implications of compiled languages.
It might be less of a sticking point if
...because declarations similar to private x are what other languages use (notably Java), and imply that access would be done with this.x. Assuming that isn't the case (see other FAQ questions), in JavaScript this...
were re-worded like:
"... because declarations similar to private x
are what other languages use (notably Java), and are closely associated with access using this.x
in those languages. Since it is a requirement that private fields must remain undetectable, they must use a different namespace than this.x
. This opens the possibility that an unintended omission of the private field operator #
..."
Like this, there are no dubious implications on which to found a counter-argument. People like myself won't agree that the problem potential is major because there appears to be equal risk that the sigil will be accidentally omitted even in the current syntax. However, that's a matter of opinion regardless of which side of the argument you're on and not worth arguing at this point.
By the way, it may be just my opinion but: The hard-to-detect issues that come up when a 1 character sigil is dropped is actually a very strong argument against use of a sigil at all. If it can be so easily omitted, then either something more obvious needs to be used instead, or the requirement that necessitates the sigil needs to be re-evaluated for its relative merit.
Not all of that is in the FAQ. Third approach certainly is, but not the first 2.
I copied all three of them straight from the FAQ: Why not give the this
keyword special semantics?, Why not just always have obj.x
refer to a private field inside of a class which declares a private field x
?, and Why aren't declarations private x
? respectively.
were re-worded like:
I think making mention of "a different namespace" would probably confuse more people than it would enlighten, but I'd be happy to change "implies that" to "are closely associated with" if that's something other people are finding confusing.
But on balance this design still seems best to us; no design is without some costs.
@bakkot
I hope you stop simply say "best to us". Who is "us"? I know there are delegates in TC39 don't agree with many aspects of this proposal. So please change "us" to "me" or the names you know really agree you.
And "no design is without some costs" --- this is a sentence without any useful info. I think many in the community want to see the explanation why the costs of one design is lower than the costs of other design.
All delegates in TC39 chose not to object to consensus, so every single member is included in “us”. That some of us may have reservations, or may have preferred a different design, on any arbitrary proposal does not alter that.
@ljharb You may explain like that, and it may theoretically valid, but it's very misleading to the community that normal people would believe all delegates have been carefully considered all aspects of this proposal and have the real consensus in every important issues. Actually many delegates never care about this proposal or just tired to block it again and again and decide let it go.
You’re right that there’s of lots of work we need to do to better convey how the process works to the community - but that’s tangential to this proposal.
No. The process is definitely related to this proposal. The failure of this proposal is due to the failure of the process. And it's no way to fix this proposal until we can fix the process first.
This proposal hasn’t failed - that’s just the subjective viewpoint of you and some others who’ve commented. The proposal has succeeded by virtue of it meeting all the constraints, and achieving consensus after years and years of debates.
@ljharb
meeting all the constraints
Enumerate please.
I can’t, because i can’t speak for every delegate, but I’m also not sure what that would achieve except create a new wave of attempts to chip away at the consensus the proposal has achieved. I’m pretty skeptical that a single consolidated list would convince all of you to happily go on about your day, given the treatment the FAQ and our answers in these threads have gotten.
that’s just the subjective viewpoint of you and some others who’ve commented.
Sincerely, if there were some way which you may agree to do a poll within selected groups of js programmers, and 60%+ people thought this proposal will be the new bad part of JS, would you agree this would be a failure of this proposal?
I can’t, because i can’t speak for every delegate...
You shouldn't need to, and I'm not asking you to. However, you said
The proposal has succeeded by virtue of it meeting all the constraints...
That means you are somehow certain of the satisfaction of all constraints that were placed on this proposal. To be certain of that requires knowledge of all the constraints. If such knowledge does not exist, then such a certainty cannot exist. So, given a good faith understanding of how you've done things thus far, one of the 3 statements below must be true. Either:
I'm curious to know which it is.
I’m also not sure what that would achieve except create a new wave of attempts to chip away at the consensus the proposal has achieved.
If it did trigger that, why would that not be a sign that something is wrong with any or all of the design, the requirements, and the process behind this proposal? There are likely some of us for whom seeing that list will actually quell their concerns. There are equally likely some of us who will be able to reveal new issues with this proposal, ones you haven't already "discussed ad nauseam". In the interest of making the proposal as usable as possible for the community, why would you not want that?
I’m pretty skeptical that a single consolidated list would convince all of you to happily go on about your day
To me, that's circumstantial evidence that there's something in the list that would indeed ignite a new round of arguments. Again, why would you not want that if you're trying to produce something developers will want to use and keep? No matter how many decades of development experience I collect (at 3 now), I'll never allow myself to become so jaded as to think that I couldn't benefit from outside opinion, regardless of how late in the process it is.
All of those three bulleted statements are true.
@rdking as I have said several times, I don't think a list of constraints would be particularly meaningful. A lot of things, like "we shouldn't add further special semantics to an already confusing keyword like this
", would not necessarily have occurred to anyone to add to the list until someone made a proposal violating it. Other things, like "this should be easy to use", are necessarily somewhat difficult to measure objectively. In practice what happens is that people suggest ideas and other people point out things they dislike about them, and whether or not those things were on some list somewhere ahead of time makes absolutely no difference. (And shouldn't - there is no reason to say "well, it didn't occur to us earlier that this was a constraint, so I guess we have to forego it".) And, indeed, that's what has been happening in these repos for the last several years (and elsewhere for the last two decades).
I could extract a list of some of the constraints which have become obvious from the FAQ from you, but to be frank copy-pasting things from the FAQ is getting a little tiring.
You've taken it on faith that the relenting of those in TC39 with dissenting opinions is due to their constraints being met and not due to things like fatigue over the issue, a desire to get something released regardless of the issues it may cause, political pressure, etc..., or
Yes, the process only works if people follow it. I trust fellow delegates to follow the process. We could accomplish nothing without this trust.
If it did trigger that, why would that not be a sign that something is wrong with any or all of the design, the requirements, and the process behind this proposal?
Why would it be? People get really frustrated when languages make decisions they dislike and will spend a lot of time trying to argue their position. These debates get reignited again any time language designers explain their decisions. This happens every time there is anything remotely controversial, and is not informative of anything.
There are equally likely some of us who will be able to reveal new issues with this proposal, ones you haven't already "discussed ad nauseam".
There is no reason to think that having a list of constrains would make any difference to this. If the problems exist, they already exist.
@bakkot Why do you think gathering requirements is the first step in software development? Isn't it to ensure as best as possible that a focused and efficient development process will be used to create something that maximally achieves the desired goals?
A lot of things, like "we shouldn't add further special semantics to an already confusing keyword like this", would not necessarily have occurred to anyone to add to the list until someone made a proposal violating it.
If you(TC39) were keeping a list of requirements/constraints and using an agile process, you would've simply been able to add on the new constraint when someone thought about it. Then you could've adjusted the design accordingly.
Other things, like "this should be easy to use", are necessarily somewhat difficult to measure objectively.
No one can reasonably expect an objective measure of a subjective ideal. However, there's nothing stopping you(TC39) from better defining what "easy to use" means. For instance:
etc.... These are all smaller details that factor into the "ease of use". When you're being vague, of course there's little you can to make something more objective. However, in that little, there's always the option of subdividing the gray area into small spots of near black and almost white.
Why would it be? People get really frustrated when languages make decisions they dislike and will spend a lot of time trying to argue their position.
While what you're saying is true, it's still none-the-less indicative of a problem. The problem may not be in the proposal but rather the proposal itself. What good does it do to build a new kind of device for mass production that you can't even break even on because not enough people will buy it? Haven't you noticed the movement away from OO and towards FP in JS? That means the target audience for this proposal is an ever shrinking # of developers. Did any of you consider that a controversial proposal that the reasonably knowledgable members of the community might consider one of "the bad parts" will end up furthering the drive toward FP, thus rendering all of your work on this proposal relatively moot?
So why would it be? Because the biggest automatic constraint to any new language feature is that enough developers have to want or need to use it.
@ljharb A quick test of your convictions:
All of those three bulleted statements are true.
- You actually believe that an absence of a veto is tantamount to an explicit agreement.
With regard to the [[Set]]
vs [[Define]]
debate, are you willing to explicitly agree that the consensus of the board in siding with [[Define]]
is the superior choice between the two?
@rdking I think that advancing this proposal is the superior choice; while I would have preferred that be done with [[Set]]
, the only way it was possible was with [[Define]]
. My preference for a different outcome does not in any way alter that I explicitly joined the consensus for this proposal as-is.
@ljharb Um.....
- You've taken it on faith that the relenting of those in TC39 with dissenting opinions is due to their constraints being met and not due to things like fatigue over the issue, a desire to get something released regardless of the issues it may cause, political pressure, etc...
My preference for a different outcome does not in any way alter that I explicitly joined the consensus for this proposal as-is.
You're right, it doesn't. But it does mean that you gave up your position due to a non-technical reason. It wasn't a matter of superiority of the decision, but rather that you were unwilling to jeopardize the release over that decision. You may not want to see this as a contradiction of your earlier comments,
The proposal has succeeded by virtue of it meeting all the constraints...
All of those three bulleted statements are true.
but others, myself included, will not be able to see it that way.
Technical reasons aren't the only, or necessarily the most important reasons, we employ. Languages are designed for humans.
Can you give an example, that occurred during the creation of this proposal, as well as a break down of when a non-technical reason outweighed all technical reasons?
Btw. "Languages are designed for humans" to communicate. Programming languages are therefore languages designed to allow a human to communicate an action to a computer as a sequence of instructions. As such, the technical reasoning should be primary. Why? While a human can learn to do almost anything, a computer has definite, well-defined limits on its capacity and capability, further constrained by the limits of the software on top of which the new program will run, and further still constrained by the technical limits of the language.
When designing anything, accommodate the weaker side first.
They're also for humans to communicate to other humans about the intention of the code.
True enough. That's why languages whose designer recognize this, and that code written in their programming language isn't always clear, have a provision for human-to-human communications. Comments anyone?
@littledan @ljharb Will this proposal be considered in the future? https://github.com/dslomov/typed-objects-es7
I find the FAQ's statement of
private implies this.x
being a reason, to be somewhat of an insult to developers in general.
I (and legitimate developers, are) am perfectly capable of coming from any another language, reading a paragraph or two on true privacy and separate name spaces in JS classes, and not being confused.
I don't just start coding in a new language and assume I know the language without reading the docs. I don't just blindly throw code a hope it works. That would be stupid on my part.
For those who think uncommented code is sufficient for human-to-human communication, please read https://medium.com/it-dead-inside/self-documenting-code-is-mostly-nonsense-1de5f593810f.
The combination of private and # seems to be a lot more rigorous, only use # appears not rigorous and not good-looking