-
Notifications
You must be signed in to change notification settings - Fork 113
Proposal for an alternate syntax regarding private fields #277
Comments
I just asked this in a Discord channel and they linked me here lol. It was bugging me why using '#' to declare private fields, if we have the 'static' keyword for static fields and methods... it's just not intuitive and breaks consistence. Having the public, private, protected access modifiers will make Javascript more appealing to people coming from static typed languages. Your proposal looks like the right direction. I hope this gains more votes. Cheers! |
Please read https://github.com/tc39/proposal-class-fields/blob/master/PRIVATE_SYNTAX_FAQ.md which should address many of your bullet points. |
@iczero https://github.com/tc39/proposal-class-fields/blob/master/PRIVATE_SYNTAX_FAQ.md#why-arent-declarations-private-x talks about why the Personally, additionally, I would highly object to anything that even implies that |
This is required, so 👍
See #74. This is something that can be added later, but we don't feel it's necessary now. |
@ljharb @jridgewell I don't see how using |
Because every other language that has |
New users would only really be confused once, and after they learn the relatively simple semantics of the |
In response to the dynamic access (#74 (comment)), the One of the benefits for dynamic access to private fields is to reduce redundancy: handleInput(name, value) {
this.#['input_' + name] = value;
} |
@iczero I hope I can save you a bit of time and frustration here because several of us have gone through this same conversation with the TC39 mebers multiple times. They won't change their mind on this point. |
@shannon I'd think it's worth a shot. |
Most people on the committee do not share this expectation, and you are unlikely to convince us otherwise. |
@bakkot I'd like to think developers aren't stupid. (except for myself for believing that I would in fact be able to convince committee members) |
Specifically compared to the property-but-actually-not-property-private-field thing of the current proposal where the field technically is prefixed by # but is actually stored in a different place than everything else and can't actually be indexed by normal methods, the |
I think this is a bad move. They lured us, the made us fall in love with Javascript thanks to ES6 and the class syntax, the static, the constructor(), the getters and setters, and we said _"wow, I see where this is heading to, and I love it!" only just to be told now "Nope, we not adding private/protected/public because reasons, LOL". It's like something that is broken and you keep "patching it" instead of taking it to the repair shop, or buying a new one. So basically the members of the "committee" don't care about what most people think because in practically every other language we have these things... K then. Keep making Javascript look like a frankestein language and keep making people make fun of it and don't take it seriously. |
It's not stupidity to read code which looks like class Foo {
private x;
setXToSecretValue() {
this.x = 10;
}
} and not notice that it does something completely different than almost identical code would do in a language like Java. Yes, people should not write this code. But some people will. And readers will be confused by it. A language which makes the Anyway, this has all been covered at extremely great length elsewhere, including in the FAQ. I don't think there is much more that can be said here. |
@bakkot I read the FAQ and many other posts, and what I gather is that you do not expect users to be able to learn the syntax of (in this case) the For preventing errors, an eslint rule can easily be created preventing the creation of normal properties when a private field with the same name already exists, unless explicitly declared beforehand. Considering that beginners to javascript are unlikely to use private fields anyways, I don't see how it would negatively affect those learning the language. For more experienced developers, they would quickly realize that their so-called "private" fields are actually very much in fact public, and would learn of their mistakes. I would specifically like to compare this solution of making private access explicit with the previous solution of including the # in the name. The latter solution causes much more long-term confusion (such as why said private field can't be indexed dynamically using Additionally, possibly the greatest drawback of the current solution is that it makes it so that the |
💀 🎏 Get it? Don't make statements like that. You're inviting trouble. I've actually proposed this exact same thing back in stage 2. It was one of 6 different possibilities I tried to sell to get out of the no-[] policy. They didn't bite back then either.
How about I throw that argument back at you. It's not stupidity to read code which looks like class Base {
a = 10;
}
class Derived extends Base {
get a() { return Math.random() * 10; }
}
let a = (new Derived).a; and not notice that it does something completely different than almost identical code would do in a language like Java. 6 of one, half-dozen of the other, right? Not really. Both clobber the developer's expectations. However, this one does so in a way so surprising that almost no one would think to check for it. |
That code would not be legal in a language like Java. |
class Application {
public static void main(String[] args) {
Derived d = new Derived();
System.out.println(d.a());
}
}
class Base {
public int a = 10;
}
class Derived extends Base {
public int a() {
return 42;
}
} Go run it! |
That's... not a getter. There is no ambiguity; the invocation at the call site makes it explicit. Anyway, this is pretty off topic. You are by this point extremely well aware that I do not consider that cornercase to be nearly as likely to come up, nor as severe if it does, as the one above. I am aware you disagree. Neither of us is likely to convince the other. This particular case does not have much bearing on the choice of syntax for private fields. Please forgive me from stepping away from this tangent at this point. |
Could we at least use both the class Foo {
private #field;
constructor(arg) {
this.#field = arg;
}
get field() {
return this.#field;
}
} |
@bakkot Either way, javascript (or rather, ecmascript) is fundamentally different from java and that should probably be the first thing someone learns when coming to javascript from. Or they're going to be tripping on a lot more things than accidentally making a field public. Further elaboration on my previous points:
|
@ExE-Boss I'd think the sigil could be optional. If people think it is clearer with the sigil, then they can use it. The main difference I am suggesting is specifically the |
@ExE-Boss That was discussed at some length and ultimately rejected mainly on the basis that it's entirely redundant as soon as you've learned that @iczero I stand by this comment. |
@bakkot And I stand by my comment that the issue you raised is substantially less confusing to anyone trying to learn javascript than to understand the weird property-but-not-really-but-it-looks-really-similar private fields thing that is what is proposed currently. Additionally, if anything, |
Oh also, following the reasoning that |
I agree, and raised similar concerns when I first became aware of this.
I think this issue continues to resurface because the decision to proceed with E.g. I have seen other suggestions and even a proposal demonstrating The current proposed syntax is not intuitive to many for multiple reasons. It would be great to comprehensively clarify why it is still considered ideal among others proposed. |
I've also been trying to push that same view, that TC39 has taken a stance not consistent with developer expectation on how ES is generally used. However, instead it seems that they're taking the conventions of compiled languages with |
Yes.
It does not especially disrupt how the language is used, and that notation is not equivalent: it implies access is being done on (We've been over this before several times in the last few years of discussion; forgive me, but I do not have the willpower to rehash this particular line of conversation for yet another time.) @ccjmne The FAQ summarizes the main points. I know @rdking does not find it adequate, which we've been over at truly incredible length at this point, but most people do. @MichaelTheriot That's been proposed several times, though I'm not going to dig it up right now. Briefly, a.) it implies that access is being done on |
Thanks for the response. Naively I would assume it means I also feel bad asking to dig up old suggestions; it would be great if they were included in the FAQ. |
@MichaelTheriot The main issue is that if you were intending to access the I guess these have come up often enough to warrant inclusion in the FAQ at this point. I don't think I will have time to get to that in the immediate future, I'm afraid. |
@bakkot I am familiar with the FAQ! I find it serves its purpose very well. NB: this isn't a realistic request, I'm being playful. |
Seems like you're still thinking of the existing |
I see, so you would need to type For what it's worth, in my mental model |
That mental model doesn't interact well with method invocation: in a suitable class, obj.#foo = function(){ console.log(this === obj); };
obj.#foo(); will print |
@bakkot Thank you. That's the first logical reason I've received for why that syntax's semantics don't work well. That's the kind of answer I've been looking for with all the other questions I've asked. |
Is it not possible that when using |
I saw this example in the read me but I've always bound methods that are put into the weakmap. var store = new WeakMap();
class A {
constructor() {
store.set(this, {
getXPrivate: function() { return this.x; }.bind(this)
});
}
x = 4;
test() {
return store.get(this).getXPrivate();
}
}
var a = new A();
a.test(); // 4
var b = new A();
b.x = 5;
b.test(); // 5
a.test.call(b); // 5 Unless I am missing something obvious I do not see why declared private methods could not be automatically bound under the hood. If not at declaration then during invocation. |
@rdking I'm not really sure what you're asking. I was talking about the mental model implied by the syntax. It looks like you're asking about the technical feasibility of a particular implementation strategy. These have very little to do with each other. (Or I don't know what you're asking, in which case, can you rephrase?) @MichaelTheriot Private methods which were declared as such could in principle be bound, though this would be a surprising difference from the already-existing public methods, but when doing something like my above code, they could not. Specifically, it is important that let f = function(){ console.log(this === obj); };
obj.#foo = f;
console.log(obj.#foo === f); print
Well, yes, in the sense that a receiver is always set during invocation. But if your mental model is that |
I see what you mean by the var store = new WeakMap();
class A {
constructor() {
store.set(this, {
getXPrivate: function() { return this.x; }
});
}
x = 4;
test() {
return store.get(this).getXPrivate.call(this);
}
}
var a = new A();
a.test(); // 4
var b = new A();
b.x = 5;
b.test(); // 5
a.test.call(b); // 5 I would think this would happen in my mental model as that is how it must correctly happen now. I assume this proposal does not introduce any new capabilities to the language outside of syntactic sugar, so it would be unusual for me to think something else is actually happening. I don't think this further complicates |
If obj#.foo = function(){ console.log(this === obj); };
obj#.foo(); prints If it is not an actual thing, then it is surprising that the syntax is I don't think this can be reconciled. |
I think that is indeed a potential flaw of
Likewise I don't think this can be reconciled with |
Yeah, my bad, but I think the mental model can be handled. If the
Done this way, the mental model stays consistent. |
Summary of more or less what I was asking for when I proposed this in the first place:
The response, summarized briefly:
Last time I checked, the FAQ (or anyone else here) didn't give any reasons as to:
|
Strongly agree with this point. Would love to see something really innovative done with it like a code comment tagging system, or code metadata. A sort of universal, open answer to stack overflow where code could be semantically indexed and searched by metadata or tags. That's just one idea, off the top of my head. '#' sign implies comments/tagging in so many places, very strange to have it used for privates, though I have read and understand the reasoning. One of the stated goals of the proposal is 100% true encapsulation of privates with no leaking. Though I understand why this is a goal, I've never really seen a discussion around a balance of benefits, and I've been following the (exhaustive) discussion since early stage. I've seen proposals that get 99% of the way there, with some small edge case leaks that have been disregarded due to not meeting the goal. However, if we take a small step back from 100% safe encapsulation as a goal, it opens up a ton of possibilities (decorator based access rules, etc...). |
Also, I've never seen it explained very well why the privates proposal was combined with the (non-controversial) public fields proposal. It's clear from the enormous amount of discussion and feedback, that privates need more work/consensus. Maybe they should be separated back out so that publics can proceed? |
Public fields could not realistically proceed without private fields since their semantics and syntax overlap with each other. That there is discussion/feedback doesn’t mean a feature necessarily needs more work, and consensus for stage 3 is already established - it doesn’t have degrees. |
What specific feedback would indicate a feature needs more work? |
I don’t think there’s a generalized answer to that. In stage 3, only feedback related to implementability, and usage feedback that was impossible to get otherwise, is expected. None of that category has presented itself for this feature as far as i am aware. |
I still don't understand why we would want a |
I do not specially dislike the actual solution but in this issue many people introduce several points that seem valid. In my case, I never liked the word "class" for javascript, I would introduce the work 'proto' or 'prototype' and use a current convention for private and protected fields. Something like this:
|
@victorherraiz see the FAQ for why any current identifier is unavailable as an option. As for another word for |
@ljharb I did not explain myself correctly. In the example I used the word |
@victorherraiz that seems like a refactoring hazard - ie if i have |
we have to face that in any refactor, without proper test any attempt of change a single line is a disaster about to happen. I understand that we have to play safe, but as I saw in many comments, we are concern that this specification will introduce another oddity into the language. Anyway, performant private fields for javascript is something that I wished for several years. |
Another way to enable fields could be use useful class decorators like |
This alternative is clearly addressed in the FAQ. We've worked hard to build a strong analogy between public and private, and we've decided not to include redundant keywords which might seem nicer when learning initially, but wouldn't really provide value over time. |
I would like to propose:
private
,protected
, etc.#
is introduced allowing access to private-type fields.#[name]
Example
Reasoning
#
symbol by itself is not directly used anymore and can be used in the future (bdistin)private
andprotected
can be put to use.#
and as such there can be a public property sharing the name of a private property, as can be done with the current proposal.#
operator is well-defined and has only one meaning (rdking)#
symbol is not part of the name, reducing possible confusion (shannon)Possible drawbacks
I don't regard this to be that much of an issue, as the
.#
operator is specifically for access to private-type fieldsI understand that this is already a stage 3 proposal and is unlikely to have changes, but I believe these changes would fix some of the problems raised by others while still retaining many of the benefits from the original proposal.
The text was updated successfully, but these errors were encountered: