Skip to content
This repository has been archived by the owner on Jan 25, 2022. It is now read-only.

Proposal for an alternate syntax regarding private fields #277

Closed
iczero opened this issue Oct 17, 2019 · 79 comments
Closed

Proposal for an alternate syntax regarding private fields #277

iczero opened this issue Oct 17, 2019 · 79 comments

Comments

@iczero
Copy link

iczero commented Oct 17, 2019

I would like to propose:

  • Private-type fields are declared using private, protected, etc
  • A new operator .# is introduced allowing access to private-type fields
  • Dynamic access can be done using .#[name]

Example

class Example {
  private a = 5;
  protected b = 2;

  getA() {
    return this.#a + this.#['b'];
  }
}

Reasoning

  • The # symbol by itself is not directly used anymore and can be used in the future (bdistin)
  • The previously reserved words private and protected can be put to use
    • Additionally, protected fields can be implemented, as opposed to the current proposal, where protected and other scopes are not usable (mbrowne)
  • Private access is done using .# and as such there can be a public property sharing the name of a private property, as can be done with the current proposal
  • Allows dynamic access to private fields (rdking)
  • The .# operator is well-defined and has only one meaning (rdking)
  • The # symbol is not part of the name, reducing possible confusion (shannon)

Possible drawbacks

  • Declaration and access isn't symmetrical (faq)
    I don't regard this to be that much of an issue, as the .# operator is specifically for access to private-type fields

I 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.

@angelhdzdev
Copy link

angelhdzdev commented Oct 17, 2019

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!

@ljharb
Copy link
Member

ljharb commented Oct 17, 2019

Please read https://github.com/tc39/proposal-class-fields/blob/master/PRIVATE_SYNTAX_FAQ.md which should address many of your bullet points.

@iczero
Copy link
Author

iczero commented Oct 17, 2019

@ljharb I have already read through the entire thing. It does not address any of my bullet points, except for the symmetry one in the drawbacks section.

@AngelHdz They linked you to the wrong place, but thanks.

@ljharb
Copy link
Member

ljharb commented Oct 17, 2019

@iczero https://github.com/tc39/proposal-class-fields/blob/master/PRIVATE_SYNTAX_FAQ.md#why-arent-declarations-private-x talks about why the private and public keywords aren't used; https://github.com/tc39/proposal-class-fields/blob/master/PRIVATE_SYNTAX_FAQ.md#why-doesnt-thisx-access-the-private-field-named-x-given-that-thisx-does indicates that dynamic private access is explicitly disallowed; to name a few.

Personally, additionally, I would highly object to anything that even implies that protected is a feature that can be used, as I feel that the feature itself is inherently inappropriate for the JS language - but I don't want to get into that yet again on github, so please feel free to reach out to me on another medium if you wish to discuss that further.

@jridgewell
Copy link
Member

jridgewell commented Oct 17, 2019

  • Private-type fields are declared using private, protected, etc

See https://github.com/tc39/proposal-class-fields/blob/master/PRIVATE_SYNTAX_FAQ.md#why-arent-declarations-private-x

  • A new operator .# is introduced allowing access to private-type fields

This is required, so 👍

  • Dynamic access can be done using .#[name]

See #74. This is something that can be added later, but we don't feel it's necessary now.

@iczero
Copy link
Author

iczero commented Oct 17, 2019

@ljharb @jridgewell I don't see how using private instead of prepending the field name with a # is a drawback. Considering how private fields are to be accessed using the .# operator (and such asymmetry is intended), I do not believe that it would be confusing.

@jridgewell
Copy link
Member

Because every other language that has private x accesses the properties with this.x. We believe that new users (who are very likely to come from another language that supports classical private) will be confused.

@iczero
Copy link
Author

iczero commented Oct 17, 2019

New users would only really be confused once, and after they learn the relatively simple semantics of the .# operator, they shouldn't be confused anymore. Compared to the current state of having a field starting with a # but actually not really being a real "property" in the traditional sense (can't be indexed by [], etc), using .# differentiates between accessing ordinary properties and private fields.

@iczero
Copy link
Author

iczero commented Oct 17, 2019

In response to the dynamic access (#74 (comment)), the .# operator would explicitly access private fields, while the normal [] operator would get properties, preventing possible confusion.

One of the benefits for dynamic access to private fields is to reduce redundancy:

handleInput(name, value) {
  this.#['input_' + name] = value;
}

@shannon
Copy link

shannon commented Oct 17, 2019

@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.

@iczero
Copy link
Author

iczero commented Oct 17, 2019

@shannon I'd think it's worth a shot.

@bakkot
Copy link
Contributor

bakkot commented Oct 17, 2019

New users would only really be confused once, and after they learn the relatively simple semantics of the .# operator, they shouldn't be confused anymore.

Most people on the committee do not share this expectation, and you are unlikely to convince us otherwise.

@iczero
Copy link
Author

iczero commented Oct 17, 2019

@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)

@iczero
Copy link
Author

iczero commented Oct 17, 2019

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 .# operator would make private access explicit, thus reducing confusion.

@angelhdzdev
Copy link

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.

@bakkot
Copy link
Contributor

bakkot commented Oct 17, 2019

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 this.x = 10 line do anything other than assign to the private field x is poorly designed.

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.

@iczero
Copy link
Author

iczero commented Oct 17, 2019

@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 .# operator for access to private fields.

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 this['#a'], how the private field is actually stored in a completely different place compared to normal properties, among others) while the former would be a one-time, mostly harmless (how much harm, realistically, can a field accidentally made public do), and relatively simple concept to learn. The current solution tries to pretend that private fields are somewhat "normal", whereas in reality, they are completely different from normal properties and should be explicitly denoted as such, and any attempt to cover up this difference simply makes it harder to understand, and by extension, to learn.

Additionally, possibly the greatest drawback of the current solution is that it makes it so that the # character is not available to use in future, likely much more major language features, which is a complaint many people have had over this proposal. I believe that the current proposal of private fields is relatively minor in comparison to other possible improvements in the future, and should not take a symbol which could be used in major new features. Private fields do not have a major effect on how anyone writes code. One simply replaces _ with #. I personally think the benefits (of which there are many) outweigh the costs (one-time confusion) when it comes to making private access explicit with .#.

@rdking
Copy link

rdking commented Oct 18, 2019

@iczero

(how much harm, realistically, can a field accidentally made public do)

💀 🎏 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.

@bakkot

It's not stupidity to read code which looks like ... and not notice that it does something completely different than almost identical code would do in a language like Java.

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.

@bakkot
Copy link
Contributor

bakkot commented Oct 18, 2019

not notice that it does something completely different than almost identical code would do in a language like Java

That code would not be legal in a language like Java.

@rdking
Copy link

rdking commented Oct 18, 2019

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!

@bakkot
Copy link
Contributor

bakkot commented Oct 18, 2019

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.

@ExE-Boss
Copy link
Contributor

ExE-Boss commented Oct 18, 2019

Could we at least use both the # sigil and the keyword?

class Foo {
	private #field;

	constructor(arg) {
		this.#field = arg;
	}

	get field() {
		return this.#field;
	}
}

@iczero
Copy link
Author

iczero commented Oct 18, 2019

@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:

  • The current syntax makes sense only superficially, beyond which it starts to become really weird.
    • The syntax makes it look as if private field accesses are more or less "normal" accesses on "normal" properties. That is probably as far from the truth as someone learning the syntax could get. They are completely different from normal properties, both in creation and access.
      • Private fields are stored in a completely different place from normal fields.
      • You can't index them by [].
      • They're not created the same as normal properties, and attempting to do so is an error.
      • Not inherited at all. In fact, they have nothing to do with the prototype system.
    • As such, it is consistent only superficially, and not internally consistent at all; it is most definitely not easy to understand past just using it (at least not when compared to the suggestion of the .# operator); and causes unnecessary confusion when things that people try just flat out don't work (that is, until they realize it was all by design).
  • The # symbol is being used, when it almost definitely will have a better use in the future.
    • This feature is relatively minor (in the FAQ it is described as basically a replacement of _ with #), and I do not believe it should just take up an unused symbol. Since I can't predict the future, I don't know which major feature would require such a symbol, but I personally do not think it is a wise choice to use that symbol here.
    • Some people believe private fields aren't even needed.
    • It can definitely be done without using up a separate symbol with minimal effort (what this proposal proposes).
  • There is only really 1 issue so far and that is regarding asymmetry, which is that people learning the language may attempt to use normal property accesses on private fields and expect them to work.
    • Comparing to the issues raised earlier, simply using a different operator for private field accesses is substantially easier to understand. You are accessing a private field or you are not. It is explicit.
    • It is a one-time thing, and mistakes are relatively harmless at that. Private fields are to be accessed using .#. People aren't going to have issues after that. After all, javascript isn't java.
    • Tools like ESLint exist and are widely used.

@iczero
Copy link
Author

iczero commented Oct 18, 2019

@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 .# operator instead of making the # part of the field name. Seeing as private fields are almost completely different from properties, it makes no sense that they should be accessed the same way.

@bakkot
Copy link
Contributor

bakkot commented Oct 18, 2019

@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 #-prefixed fields are private, which you have to learn when using the feature anyway. (Personally I am neutral-to-positive on that suggestion, but many people were opposed and I don't think there's much inclination to revisit that discussion unless there's something which has changed since the last time we had it.)

@iczero I stand by this comment.

@iczero
Copy link
Author

iczero commented Oct 18, 2019

@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, private #a; would help reserve the # symbol for better use in the future.

@iczero
Copy link
Author

iczero commented Oct 18, 2019

Oh also, following the reasoning that private a could result in people trying this.a, I see no reason why someone learning the new syntax wouldn't regard the # in #a as simply being something to denote it being private, and then proceed to use this.a anyways.

@iczero
Copy link
Author

iczero commented Oct 18, 2019

Welp. It's been about 1 year and 12 hours since #150, and more than 2 years since #10. I will follow @shannon's advice.

@MichaelTheriot
Copy link

@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.

I agree, and raised similar concerns when I first became aware of this.

this.#prop syntax imitates dot notation property accessor, which is counterintuitive because these are not properties and do not behave like ECMAScript properties. E.g.:

  • cannot be deleted
  • cannot be declared post-initialization
  • bracket notation incompatible
  • Symbols incompatible
  • in operator incompatible
  • Object.getOwnPropertyDescriptor() incompatible
  • names follow rules imposed on variables, not properties (any string value)

I think this issue continues to resurface because the decision to proceed with this.# syntax vs the various others that have been proposed is unclear, and intuition expects different behavior here.

E.g. I have seen other suggestions and even a proposal demonstrating this->prop syntax and it still remains unclear to me the reasons against it, years later.

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.

@rdking
Copy link

rdking commented Oct 19, 2019

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 class trying their best to make those the conventions of ES. The conflict between that and the nature of ES not only causes mental model issues, but actual conceptual and semantic issues in the implementation. Yet somehow they're considering the collection of those individually rather small issues to be of insignificant concern.

@bakkot
Copy link
Contributor

bakkot commented Nov 4, 2019

@rdking

Is there truly so much more value to be found in having #x pair with this.#x

Yes.

that it is worth disrupting how the language is used when there's an equivalent notation that won't cause this disruption?

It does not especially disrupt how the language is used, and that notation is not equivalent: it implies access is being done on this#, which is not a thing.

(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 this#, which is not a thing, and b.) it interacts very poorly with nested property access: what does #this.a.b mean?

@MichaelTheriot
Copy link

Thanks for the response. Naively I would assume it means (#this.a).b, although it would be helpful to know what is particularly the issue there.

I also feel bad asking to dig up old suggestions; it would be great if they were included in the FAQ.

@bakkot
Copy link
Contributor

bakkot commented Nov 4, 2019

@MichaelTheriot The main issue is that if you were intending to access the #b property of this.a, you might well reach for the same syntax. More generally, it's best if syntax is local: here the # is modifying how the . works, and so should ideally not be separated from it.

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.

@ccjmne
Copy link

ccjmne commented Nov 4, 2019

@bakkot I am familiar with the FAQ! I find it serves its purpose very well.
What I long for is a global, shared, semantically indexed mind map 🙂 or an eidetic memory.
I actually had to search online for "eidetic", that's how dire my need for it would be!

NB: this isn't a realistic request, I'm being playful.

@rdking
Copy link

rdking commented Nov 4, 2019

It does not especially disrupt how the language is used, and that notation is not equivalent: it implies access is being done on this#, which is not a thing.

Seems like you're still thinking of the existing # usage. The suggestion I mentioned makes # an operator. So this# (which shouldn't ever exist without being followed by an access, so I guess that makes it a binary operator?) should be read as private(this) with access of the private container done on this, and access of the member done on that private container.

@MichaelTheriot
Copy link

I see, so you would need to type #(this.a).b to access #b of this.a (which is what I would expect, but not as advantageous).

For what it's worth, in my mental model this# "is a thing": a store for instances that cannot be directly accessed but whose keys can be accessed in class bodies. e.g. I would consider this# the equivalent of a weakmap keyed to the current instance. Which is not far-fetched as it is the only way to translate this feature into the language today. I do not think this concept is much different than behavior from super().

@bakkot
Copy link
Contributor

bakkot commented Nov 4, 2019

in my mental model this# "is a thing": a store for instances that cannot be directly accessed but whose keys can be accessed in class bodies

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 true, because #foo is being invoked on obj. If obj# were its own thing, presumably it would be the receiver, rather than obj itself.

@rdking
Copy link

rdking commented Nov 4, 2019

@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.

@rdking
Copy link

rdking commented Nov 4, 2019

Is it not possible that when using # through [[Get]] and [[Set]], the target ends up being the private container, while the receiver remains the originating object? This kind of thing is easily modeled through Proxy.

@MichaelTheriot
Copy link

MichaelTheriot commented Nov 4, 2019

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.

@bakkot
Copy link
Contributor

bakkot commented Nov 4, 2019

@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 true. If #foo were auto-bound, it could not.

If not at declaration then during invocation.

Well, yes, in the sense that a receiver is always set during invocation. But if your mental model is that this# is a real thing, why would you expect that to happen? That's not how receivers normally work; we would really prefer not to further complicate this.

@MichaelTheriot
Copy link

I see what you mean by the obj.#foo = f test, I really had not considered the case where you change the method to a new one. I still stand that this would work:

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 this either, but extends what you currently expect from it.

@bakkot
Copy link
Contributor

bakkot commented Nov 4, 2019

If this# is an actual thing, then it is surprising that

obj#.foo = function(){ console.log(this === obj); };
obj#.foo();

prints true - why isn't the receiver this# rather than this?

If it is not an actual thing, then it is surprising that the syntax is this#.foo, since that appears to be invoking the foo method of this#, not this.

I don't think this can be reconciled.

@MichaelTheriot
Copy link

I think that is indeed a potential flaw of this#.foo syntax, to suggest that foo is a method of this#, and I am not in disagreement. Perhaps this better illustrates why I am of the belief that this#prop or this->prop is better at conveying the syntactical sugar of what is truly occurring.

this.#prop has its own issues even if it benefits here, like suggesting this['#prop'] can work (multiple people assumed so), incompatibility with property operators in and delete, and incompatibility Object.defineProperty.

Likewise I don't think this can be reconciled with .# syntax.

@rdking
Copy link

rdking commented Nov 4, 2019

@bakkot

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.

Yeah, my bad, but I think the mental model can be handled. If the # operator is introduced as 2 different notations:

  • obj #. member - for direct access
  • obj #[ memberName ] - for calculated access

Done this way, the mental model stays consistent.

@iczero
Copy link
Author

iczero commented Nov 5, 2019

Summary of more or less what I was asking for when I proposed this in the first place:

  • There is a separate operator for private access, for example, .# or -> or even .* and be used as this.#thing
  • Private fields are declared using accessModifier fieldName
  • Said operator can be used to do dynamic access to private fields, for example this.#['thing'] or this->['thing'] or even this.*['thing']
  • The weird hash thing isn't part of the field name
  • Aforementioned hash should be reserved for the future for almost certainly more important language features

The response, summarized briefly:

  • Access modifiers are bad
  • Dynamic access is bad
  • Apparently having a separate operator for private access is too confusing for anyone coming from languages that have no relation to ecmascript and is therefore also bad
  • The weird hash thing shall be part of the field name

Last time I checked, the FAQ (or anyone else here) didn't give any reasons as to:

  • why access modifiers are bad,
  • why dynamic access is bad in the context of a private access operator,
  • why a separate operator for private access is confusing, and
  • why the weird hash thing must remain part of the field name

@claytongulick
Copy link

claytongulick commented Nov 5, 2019

Aforementioned hash should be reserved for the future for almost certainly more important language features

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...).

@claytongulick
Copy link

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?

@ljharb
Copy link
Member

ljharb commented Nov 5, 2019

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.

@MichaelTheriot
Copy link

What specific feedback would indicate a feature needs more work?

@ljharb
Copy link
Member

ljharb commented Nov 5, 2019

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.

@ray007
Copy link

ray007 commented Nov 5, 2019

I still don't understand why we would want a # somewhere in there at all.

@victorherraiz
Copy link

victorherraiz commented Dec 11, 2019

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:

proto Foo {
  constructor() {
     this.a = false // public
     this._b = true // protected or private
     this.__c = 42 // private or protected
  }
}

@ljharb
Copy link
Member

ljharb commented Dec 11, 2019

@victorherraiz see the FAQ for why any current identifier is unavailable as an option. As for another word for class, I desperately wish that was the case, but it is many, many years too late for that.

@victorherraiz
Copy link

@ljharb I did not explain myself correctly. In the example I used the word proto instead of class to change the visibility rules of the _ prefix. Of course, you cannot break any current implementation. Did you consider any class marker (e.g. data class Foo) instead of # prefix?

@ljharb
Copy link
Member

ljharb commented Dec 11, 2019

@victorherraiz that seems like a refactoring hazard - ie if i have class X { /* 500 lines of stuff */ foo_ = 'public'; /* 500 more lines of stuff */ } and i stick data in front, foo_ suddenly stops being public, breaking consumers of it?

@victorherraiz
Copy link

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.

@victorherraiz
Copy link

Another way to enable fields could be use useful class decorators like sealed or frozen. Something like sealed class makes sense to force you to specify all the properties at the definition and it could make it more performant.

@littledan
Copy link
Member

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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests