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

Developer experience of the syntax and semantics of this proposal #175

Open
mbrowne opened this issue Nov 23, 2018 · 87 comments
Open

Developer experience of the syntax and semantics of this proposal #175

mbrowne opened this issue Nov 23, 2018 · 87 comments

Comments

@mbrowne
Copy link

mbrowne commented Nov 23, 2018

This new issue is intended as a continuation of the recent discussion in #100. (The only reason for a new thread is that the original has become very, very long, which among other things makes it less approachable to anyone not already participating in the discussion.)

VERY IMPORTANT NOTE: Anyone who would like to comment here should first read the PRIVATE SYNTAX FAQ. This will save everyone time. Thank you.

@nabil1337
Copy link

nabil1337 commented Nov 24, 2018

Hi,
I've been programming with JS for about six years now. I never intended to influence the standard, because most new things were indeed quite useful and made sense. But I have a feeling that the language is now being bloated with too many and partially weird things, just like the # char to declare private fields.

The strengths of JS are - among others - that it's simple and easy to learn for devs coming from C/C++, Java or PHP.

So, we have syntax and classes like in some of these languages. Why not just do something like:

class MyClass {
    myVar = 3; // public field
    public otherVar = 4; // also public field
    private yetAnotherVar = 5; // private field

    protected myMethod () {
        // do things here...
    }
}

As you can see, adding access modifiers is optional in this example, the default one being public.
Public fields will simply be properties on the instance object that can be accessed normally from outside the class, whereas attempting to access private/protected from outside will throw an error e. g.:

const myObject = new MyClass();

myObject.myVar; // returns 3
myObject.yetAnotherVar // throws Error

And while I'm at it, athough this is OT:

function myFunc (String name, Number age, cityOrCode) {
    // name is definitely a String
    // age is definitely a Number
    // cityOrCode can be any type
}

let myVar = 3; // normal variable, any type
let String otherVar = 'some string'; // can only be assigned a string type

My point is: JS should be simple and not have unexpected, weird syntax. Development of the language should focus on making a developers life easier and allowing noobs to quickly understand code. In my opinion, it's better to not have a feature, than to add a weird or complicated one (Object.assign, # for private, ...)

@ljharb
Copy link
Member

ljharb commented Nov 24, 2018

It would be unexpected and weird if "private" things were publicly visible properties that threw on access, exposing their existence.

@nabil1337
Copy link

It would be unexpected and weird if "private" things were publicly visible properties that threw on access, exposing their existence.

Good point. Maybe make private and protected fields not enumerable. Though I think this would only be an issue in those cases, where you attempt to add random properties to instance objects - which is bad style anyway, e. g.:

let myInstance = new MyClass();
myInstance.someRandomProp = 3; // bad style, unless someRandomProp is a known public field.

@ljharb
Copy link
Member

ljharb commented Nov 24, 2018

Whether it's a bad style or not isn't the point - it's a core feature of javascript that objects are mutable unless otherwise made immutable, and privacy isn't related to mutability.

@nabil1337
Copy link

Whether it's a bad style or not isn't the point - it's a core feature of javascript that objects are mutable unless otherwise made immutable, and privacy isn't related to mutability.

I would expect privacy to be related to mutability, just as it is the case in other languages. If the standard doesn't yet allow this to be the case, shouldn't it be adjusted first? In other words: If the standard limits you from implementing fields the easy way, why would you implement them in a hard to grasp way instead of adjusting the standard first, before attempting to implement fields?

@ljharb
Copy link
Member

ljharb commented Nov 24, 2018

JS is constrained by backwards compatibility, so no, that's not something that can ever be changed.

@mbrowne
Copy link
Author

mbrowne commented Nov 24, 2018

@nabil1337 It sounds like you might not have seen or read the FAQ about the private fields syntax. Actually this prompted me to add a disclaimer to my first post here encouraging everyone to read that FAQ as a prerequisite for commenting on this thread. This is not intended to be overly strict or unwelcoming but simply to make the discussions here more productive. This is especially important given that the private field syntax in particular has been discussed very extensively especially in the past couple years, and the syntax you suggested has already been suggested by many others and comes with some very significant tradeoffs that have been discussed many times.

Of course, as you can see by reading through comments in #100, the syntax is still quite controversial even among those who are aware of the tradeoffs. But it's important to realize that syntax like this:

class MyClass {
    private myPrivateField = 1; // private field

    someMethod() {
       console.log(this.myPrivateField);
    }
}

...would actually make it impossible to have fields that are 100% private. This relates to another past discussion that you might want to familiarize yourself with, regarding the desire for "hard private" and the tradeoffs between hard private and soft private: #136. And just to counterbalance what @ljharb said, in many other languages you can always access private properties via reflection, so private doesn't have to mean 100% hard private, but the committee has landed on hard private being an important requirement especially for library authors (for more on that as well as counterarguments, see #136 as well as earlier discussions in #100 and other threads).

After years of careful deliberation, this class fields proposal is already in stage 3 which means it's highly unlikely that it will be changing much. The main reason for continued discussion is that there are still some alternative proposals that some community members are interested in, primarily these three:

As @littledan (a committee member) has communicated, the TC39 committee has reaffirmed consensus on the class fields proposal multiple times, so keep in mind that these alternative proposals are unlikely to succeed... but since many people still have concerns with the current proposal, there is still interest in discussing them, improving them, and advocating for them in the hope that the committee might hold off on a final decision and reconsider other options one more time.

(Side-note: Personally I am more interested in advocating some smaller adjustments to the current proposal rather than rejecting it completely, and I believe the use of a prefix like # is the best possible option for private fields given the constraints. But the above summary is not a reflection of my opinion but rather my attempt at a realistic description of the situation right now, so as not to give anyone unrealistic expectations.)

@nabil1337
Copy link

nabil1337 commented Nov 24, 2018

@mbrowne Thank you for explaining! Sorry I didn't notice the FAQ.

I have some remarks:

The FAQ states:

Having a private field named x must not prevent there from being a public field named x, so accessing a private field can't just be a normal lookup.

It explains this as follows:

If private fields conflicted with public fields, it would break encapsulation; see below.

Futher:

Library authors have found that their users will start to depend on any exposed part of their interface, even undocumented parts.

The simplest solution would be to just throw an error when trying to access these special fields, as I proposed.
It doesn't really matter if someone knows that there is a private field foo, as long as they are unable to get it's contents. This is probably also what #136 meant.

My approach:

  • more similar to other languages, faster to learn
  • not propagating the anti pattern of having duplicate field names (imagine forgetting the '#' or adding it when not needed)
  • not having to deal with this['#x']
  • allowing for protected
  • no weird characters involved

I know, this has apparently been discussed for years. But as a normal dev, you don't really get to know all these things too quickly. And please consider the largely negative feedback from the recent discussion. Shouldn't the people who actually use the language be able to decide if they want this proposal? Thanks for your time.

@ljharb
Copy link
Member

ljharb commented Nov 24, 2018

Yes, it absolutely matters, because it means adding, renaming, or removing private fields becomes observable and thus part of the public api. Its contents are irrelevant, its mere existence is information that must not be obtainable.

@bakkot
Copy link
Contributor

bakkot commented Nov 24, 2018

In addition to @ljharb's comment, it would also be really annoying from an ergonomics point of view if a private field on a base class prevented a similarly named public field or method on a subclass. Among other things, it would mean that adding a private field would always be a potentially breaking change.

Shouldn't the people who actually use the language be able to decide if they want this proposal?

A great many people involved in this discussion actually use the language quite heavily both personally and professionally, myself included.

@kaizhu256
Copy link

operationally, how many of you would actually enjoy maintaining web-projects with polymorphic-classes with subclassed-properties having the same names as [unobservable] private-fields?

this feels like a low-level hammer for low-level general-purpose programming things encountered in java/c++, not a high-level glue-language like javascript used by most of us primarily for baton-passing serialized JSON-objects around (with async static-functions).

@trotyl
Copy link

trotyl commented Nov 24, 2018

operationally, how many of you would actually enjoy maintaining web-projects with polymorphic-classes with subclassed-properties having the same names as [unobservable] private-fields?

@kaizhu256 The real problem is, one would never know what private fields are used in base class as they're not part of your project, and they're also not documented as not being part of the public API.

So that whenever trying to extend an external class, your derived class would break weirdly. Even it doesn't break now (when you writes them), when the base class add a new private field suddenly your class is broken.

@kaizhu256
Copy link

@trotyl, the problem is deeper than that. i feel the whole class-based approach to solving async, UX-workflow problems commonly encountered in javascript is flawed.

class-methods are ok for blocking-code design-patterns in java/c++, where adding new features simply means inserting new blocking code between-the-lines of existing blocking code.

class-methods are not ok in async UX-programming where commonly added features like autocomplete, autovalidation, file-upload-progress, etc... often involve rewriting the entire ux-workflow (and any io-based class-methods with it). most of these tasks are more elegantly solved with callback-based static-functions.

@mbrowne
Copy link
Author

mbrowne commented Nov 24, 2018

@ljharb

Yes, it absolutely matters, because it means adding, renaming, or removing private fields becomes observable and thus part of the public api

It's a bit of a stretch to say it's part of the "public api" if that weren't the author's intention at all and they indicated it with private, but this is indeed an important consideration for anyone writing classes that will primarily be consumed by others. I found the code example in this comment by @bakkot quite helpful for understanding this issue.

Another significant issue is that without a prefix, the interpreter wouldn't have an immediate way to distinguish between private and public at the location in the code where a field is being accessed. For a compiled language this wouldn't be a big issue, but since JS is an interpreted language, it would make all property/field access more expensive (at least at class evaluation time). @nabil1337 that's why your suggestion of just throwing an error if someone attempts to incorrectly access a private field is not as great of a solution as it seems, because the interpreter would have to check for all possible base classes and prototype modifications on every private field declaration to make sure there isn't already a public field with that name, and some of the accesses as well (at least the dynamic ones).

For these reasons, even the alternative proposals that community members here are still seriously considering include dedicated syntax for accessing private fields. There is also one other option that I think is very much worth considering, which is keeping # but adding the keyword private to the declarations (e.g. class MyClass { private #x = 1; ... }). That would make the syntax more consistent and easier to explain in the future if other access levels besides public and private were ever added.

BTW @nabil1337, I'm not sure if you're already aware of this, but this['#x'] is not a valid way of accessing a private field in this proposal. That would access a public property named '#x'. This is an unfortunate but necessary consequence of the rest of this proposal given the need for backward compatibility.

@mbrowne
Copy link
Author

mbrowne commented Nov 24, 2018

@nabil1337

Shouldn't the people who actually use the language be able to decide if they want this proposal?

The committee members are fortunately very familiar with real-world usage and concerns, thanks to first-hand experience as @bakkot mentioned. (They have also done community outreach outside of this repo, which the committee members could speak to better than me.) As for me, I use the language every day as a frontend (and sometimes full-stack) web developer and I am not a member of the committee, just someone who noticed this github repo a while back and started participating in discussions. Despite a few lingering concerns, I think this proposal is still better overall than any alternative; unfortunately we're fairly constrained by the existing language in some ways and no solution is going to be perfect. There are plenty of other members of the community who agree. However, they're not well-represented here because those who are satisfied with the proposal have less motivation to comment. As to the great number of people who continue to oppose the # syntax, it's unclear how many of them have fully looked into the consequences of alternative options such as the syntax you suggested. Certainly some people have fully considered the issue and still object to #, but it's important to learn about the tradeoffs that come with each of the alternatives.

@nabil1337
Copy link

Thanks for all the replies.

@ljharb

Yes, it absolutely matters, because it means adding, renaming, or removing private fields becomes observable and thus part of the public api. Its contents are irrelevant, its mere existence is information that must not be obtainable.

It's not part of the api if it's private. Knowing about the existence of a private property that you cannot access doesn't really matter in day to day programming.

@bakkot

In addition to @ljharb's comment, it would also be really annoying from an ergonomics point of view if a private field on a base class prevented a similarly named public field or method on a subclass. Among other things, it would mean that adding a private field would always be a potentially breaking change.

That's an important point. If in a child class, it must be possible to define a field or method by the same name. If we look at PHP (by far not my favorite langauge, but widespread), we are allowed to have this code:

class ParentClass {
    private $field = 1;

    public function print () {
        echo $this->field;
    }
}

class ChildClass extends ParentClass {
    public $field = 3;
}

$child = new ChildClass();
echo $child->print(); // prints 1
echo $child->field; // prints 3

This is intuitive, because the parent method can fulfill the author's intent without being misled by whatever the child classes' field value is.

@mbrowne

that's why your suggestion of just throwing an error if someone attempts to incorrectly access a private field is not as great of a solution as it seems, because the interpreter would have to check for all possible base classes and prototype modifications on every private field declaration to make sure there isn't already a public field with that name, and some of the accesses as well (at least the dynamic ones).

Not quite sure if I understand you here. Isn't this a one time scan that could easily be stored in memory?

I'm not sure if you're already aware of this, but this['#x'] is not a valid way of accessing a private field in this proposal.

That's why I pointed it out as a disadvantage. Because it's inconsistent: You can do this.#x but not this['#x']. But then you can do both this.x and this['x'].

As to the great number of people who continue to oppose the # syntax, it's unclear how many of them have fully looked into the consequences of alternative options such as the syntax you suggested. Certainly some people have fully considered the issue and still object to #, but it's important to learn about the tradeoffs that come with each of the alternatives.

I agree. I might not be aware of all consequences yet, this is why I'm hoping to learn. But it's not easy to find the right place to start. Information seems to be scattered to serveral issues and text files.

One more question I couldn't find an answer to: What about protected fields and methods?

@mbrowne
Copy link
Author

mbrowne commented Nov 24, 2018

Not quite sure if I understand you here. Isn't this a one time scan that could easily be stored in memory?

I don't have personal experience with the inner workings of the JS engine (aside from outwardly observable behavior) so there are others who would be better qualified to describe this in detail. But I do know that unprefixed private fields (sharing the same namespace as public fields) would complicate the current semantics and add some amount of additional overhead, whereas the current proposal would keep the current semantics for public property lookups unchanged. Yes, the field declarations at the top of the class could be scanned once and stored in memory, but there are more significant differences when it comes to the places (call sites) where fields are accessed. # immediately signals a private field meaning public and private could be distinguished more efficiently. Another thing: when I mentioned "dynamic" lookups I was referring to something like this:

obj[myPropertyName]

If public and private shared the same namespace, then I don't see how such dynamic access could work in all cases without requiring extra checks for private fields at run-time.

Anyway, all of this is moot given that the ability to detect the presence of a private field from outside a class has been deemed unacceptable by the committee. Truly hard private fields are apparently one of the most frequently and emphatically requested features from library and framework authors (for reasons discussed in many previous threads, although I'm sure someone could summarize them if you can't find them easily).

@mbrowne
Copy link
Author

mbrowne commented Nov 25, 2018

One more question I couldn't find an answer to: What about protected fields and methods?

Here's a link to a fairly recent issue specifically about protected:
#122

More recent discussion:
rdking/proposal-class-members#3 (comment) (note: external link)
#150 (comment)

My opinion:
First of all, I agree with a point that some committee members have made in the past: even if protected were viable for JS (which is questionable given the lack of strict typing), access modifiers should be based on real-world usage and feedback since JS is unlike other OOP languages in important ways. It would be really good to be able to work with private fields for a while to figure it out and ensure a good decision—including whether or not intermediate access modifiers like protected are actually needed in the first place. The use of decorators (currently in stage 2) in combination with private fields gives a lot of interesting possibilities for sharing and enabling reflection on private fields. Perhaps private fields + decorators will be sufficient, but if not they can still help provide very useful feedback that can inform future proposals. I also believe that some sort of module-scoped access level for fields (could be called internal) might be more generally useful and appropriate for JS than protected (friend could also be useful), but regardless, there are important use cases for multiple classes sharing private members internally.

But most of all we need to make sure we're not painting ourselves into a corner with this proposal:
#144 (comment)

On the other hand, maybe we're not painting ourselves into a corner but just limiting ourselves a bit (but I'm not fully convinced):
#122 (comment)

@mbrowne
Copy link
Author

mbrowne commented Nov 25, 2018

For completeness I'm also including a link to a very relevant comment by @bakkot, although I remain skeptical that private fields + decorators will be a fully acceptable solution for intermediate access levels in the long term:
#144 (comment)

@SCLeoX
Copy link

SCLeoX commented Nov 25, 2018

The only reason why hard private is desired over soft private I can find is this from FAQ:

Library authors have found that their users will start to depend on any exposed part of their interface, even undocumented parts. They do not generally consider themselves free to break their user's pages and applications just because those users were depending upon some part of the library's interface which the library author did not intend them to depend upon. As a consequence, they would like to have hard private state to be able to hide implementation details in a more complete way

Honestly, it only explains why encapsulation is required. I don't see why you need to hide all those private members. You can always find out what are those via reading the source code anyways, even if there is hard private. In the reality, you only want to prevent users from "depending upon" those undocumented APIs. To me, just disallowing developers from accessing them is more than enough. (I actually prefer TypeScript style compile-time encapsulation without runtime enforcement. Private should just be an advice instead of limitation. Personal opinion though.)

@ljharb
Copy link
Member

ljharb commented Nov 25, 2018

@SCLeoX it's easy - and more common than you'd think - for users to write code that branches on the mere presence of public properties as a brittle means of feature/version detection. For those of us that want to truly avoid breaking changes in a non-major version, preventing access is not nearly enough.

@SCLeoX
Copy link

SCLeoX commented Nov 25, 2018

@ljharb I don't get it. ._. So what's the problem with feature/version detection... If there is a change, there is a way to detect it. Otherwise, it will be the exact same thing. ._.

Even if the "hidden change" requires a password to unlock, you can always .toString to get the source code of the function.

@ljharb
Copy link
Member

ljharb commented Nov 25, 2018

@SCLeoX the point of private implementation details is that the maintainer is/should be free to change/refactor them in any way they like that doesn't change the observable behavior - the API.

.toString is a separate issue, that can be handled by using .bind or Proxy, but in practice, isn't really a concern.

@SCLeoX
Copy link

SCLeoX commented Nov 25, 2018

@ljharb If no observable behavior is changed, why there is a change in the first place... If there is a change in the code, something should change in its behavior - let it be performance improvement or bug fixes. If the behavior is exactly identical, for example, renaming a local variable, there should not be a new release of that library anyways.

@mbrowne
Copy link
Author

mbrowne commented Nov 25, 2018

@SCLeoX What if the library author renamed or removed a private field in the process of reimplementing something to fix a bug, but otherwise the public API remained unchanged?

@mbrowne
Copy link
Author

mbrowne commented Nov 25, 2018

A solution that could be technically viable but probably not practically viable would be to use private x for soft private (which would translate to symbols or something similar) and #x for hard private. The problem is that having two kinds of native syntax for private could be confusing, and not everyone agrees on which is the better initial choice when writing a class that will be consumed by others. With hard private fields, you'll be able to use a decorator to expose them, e.g.:

@reflect  // an example decorator that would make `#x` available to a reflection API
#x

I think that makes more sense semantically than the other way around (i.e. it makes more sense than a decorator that would somehow turn a symbol into a hard private field).

The unfortunate part is that class authors don't always anticipate the need for reflection. So even in cases where reflection would be very useful and done with the full expectation that the author could change the internal implementation at any moment, it simply wouldn't be an option unless the class author decorated those private fields. This is worsened by the fact that the decorators proposal was recently changed so that you can no longer access private fields via a decorator on the whole class (due to concerns about leakage of encapsulation), but would have to decorate each field even if your intent is just to make all fields available for reflection.

In some ways I think it would be better if soft private were the default, but OTOH, exposing internal implementation details that some developers might not even realize they're exposing is arguably worse, so hard private is a better default from that perspective. Decorating each field to enable reflection could be a bit tedious especially for classes within your own code base that will never be consumed by a 3rd party, but I haven't seen many use cases for reflection of private state within the same code base in the first place.

@kaizhu256
Copy link

but OTOH, exposing internal implementation details that some developers might not even realize they're exposing is arguably worse, so hard private is a better default from that perspective.

how many js-devs are naive enough to believe it's practical to audit webapps with hard-privates/encapsulation to guarantee against data-leakage when mixed with 3rd-party code? nobody. security-arguments for private-fields in javascript are laughably impractical.

@ljharb
Copy link
Member

ljharb commented Nov 25, 2018

Plenty are, and plenty do it, myself and my company included. I’m sorry your experience is with devs that don’t, but that doesn’t mean your experience is universal nor does it give you the right to be condescending.

@kaizhu256
Copy link

@ljharb nobody mixes crypto, credit-card, password, etc. with untrusted javascript code regardless if it's encapsulated or not. one does not see untrusted-code/ads running on merchant's payment webpage, and private-fields is not going to change that.

sensitive data has and will always be handled in javascript by running it in isolated webpage (client-side), or isolated at network-interface level (server-side). but never at code-level.

@Igmat
Copy link

Igmat commented Nov 27, 2018

Quotes with my own opinion from that article:

Despite all of the above, I am not an irreconcilable opponent of the [[Define]] semantics (although I would prefer [[Set]]), because it has its own positive aspects. However, unfortunately, its advantages do not outweigh the main disadvantage - we have been using [[Set]] semantic for years, since it's used in babel6 and TypeScript by default.

While encapsulation is required for almost all types of code, brand-check has very limited number of use cases. Mixing them into one syntax will lead to appearing of dozens of unintended brand-checks, when developer just wanted to hide implementation details.

And In lieu of conclusion part of the article.

Everything else is facts without judgement and interpretation, just facts. @slikts (or @mbrowne) are you able to prove the opposite? If yes, please go ahead and create your article (or just a comment here in github), which shows logic flaws or false facts in my review. If not, you have to confess that my article is more neutral than FAQ, README and presentation made by committee till this moment, because it doesn't hide issues under the carpet.

@Igmat
Copy link

Igmat commented Nov 27, 2018

I don't want to blame or offend anybody, but I think that supporters of existing proposal see my article as not impartial, because while reading they feel proposal becoming not as good as they felt it before. And the easiest explanation is that author isn't neutral and not that biased reader had wrong assumptions or missed something important during initial evaluation of current proposal.

@slikts
Copy link

slikts commented Nov 27, 2018

This just isn't serious. The article is blatantly not neutral; it gives your preferred interpretation even in the title. There can't be a discussion if the basic facts can't be agreed upon. The relevance of neutrality is that it's a starting precondition for reliable polling, but just starting; beyond that there's complex questions like how to control for self-selection bias, etc. Bringing up the FAQ and readme is just deflection.

I don't want to blame or offend anybody, …

Having to include a disclaimer like this should have been taken as a clue that it's not appropriate.

@mbrowne
Copy link
Author

mbrowne commented Nov 27, 2018

@Igmat I agree that the readme of this repo is not neutral, but I don't think it has any obligation to be. Do you think every TC39 proposal should be required to include a long list of downsides and potential downsides in its readme rather than simply stating the purpose of the proposal and making a case for it? As to the documentation in this repo in general (not just the readme), could it be improved by better documenting tradeoffs and drawbacks of the proposal? Certainly. But again, I don't think it has an actual obligation to do so. If the community were to put together a list of tradeoffs that fairly covered both sides of the argument and posted it somewhere, then it would be reasonable to request that there be a link to it in the readme, but that's about it. Expecting a full documentation of not only the pros but also the cons of the proposal is simply asking too much of the committee, and the community already has plenty of opportunity to bring up problems and concerns in these discussion threads.

And finally, just because the documentation of this repo isn't as impartial as it could be doesn't mean that when conducting a survey you should focus more on the counterargument. Regardless of whether the documentation here were neutral or completely biased, the correct way to do research is to just stick to the facts and/or present both sides as neutrally as possible.

@Igmat
Copy link

Igmat commented Nov 27, 2018

@slikts, @mbrowne remove three paragraphs I mention before as my own opinion and add actual possibility to declare public and private fields for classes line to the begining and you'll get a list of tradeoffs that fairly covered both sides of the argument, becasue everything else in my topic ARE FACTS.

But I'm not trying to convince anybody that my research is 100% correct, even though I doubt that such change to the article will significantly affect survey results.

BTW, you are welcomed to make your own survey - I'll provide you with my text in .md format if needed and you may change it in any way you think appropriate and publish it anywhere even without a link to me or my article.

I just fulfilled the gap in argumentation of opponents of existing proposal.

So what do we have now?

Argument Committee Opponents
Technical - +
Feedback - +
Readiness + -

@robbiespeed
Copy link

The community feedback of the proposal continues to show # sigil to be undesired. There is a lot of concerned feedback both in the issues of this repo, and whenever it get's mentioned in community forums see this latest post on reddit about babel support. What worries me the most is the only defence being brought forward, is that the sigil is the only proposal that allows for hard private. We know that not to be the case, there have been suggested alternatives with near identical feature parity even.

I fail to see why Private Symbols are not adopted in place of the sigil syntax, for 3 main reasons:

  • Private Symbols don't have a controversial syntax
  • Less to teach, semantics of symbols are already known
  • Greater flexibility

To show what I mean by greater flexibility, something that is not possible with the sigil syntax.

const sizeHandlerSym = Symbol.private();

Array.prototype[sizeHandlerSym] = function () {
  return this.length;
};

Set.prototype[sizeHandlerSym] = Map.prototype[sizeHandlerSym] = function () {
  return this.size;
};

function getSize (collection) {
  return collection[sizeHandlerSym]();
}

class Foo extends Map { ... }

getSize(new Foo());

@mbrowne
I agree that the readme of this repo is not neutral, but I don't think it has any obligation to be. Do you think every TC39 proposal should be required to include a long list of downsides and potential downsides in its readme rather than simply stating the purpose of the proposal and making a case for it?

Yes, I think they should be required to include drawbacks, and/or alternatives sections. Many RFC processes already do this. It seems like the FAQ attempts to cover this, but ends up being one sided, by only selectively showing alternatives that fail. IMO this is even worse, because it falsely props up the proposal.

@littledan
Copy link
Member

I agree that we should have an open discussion about pros and cons of proposals, and would appreciate your help in collecting this in general. For future proposals, let's try to collect this input earlier in the process, prior to Stage 3, so that it's easiest for TC39 to take it into account. At some point, though, a conclusion should be drawn, and a rationale published.

@mbrowne
Copy link
Author

mbrowne commented Dec 7, 2018

@littledan Does this mean that the committee is working on a rationale document explaining the decisions that were made on this proposal? I think we have some sense of individual arguments in favor of certain points, e.g. hard privacy and the syntax, but what's missing is the big picture, particularly why this proposal was chosen instead of alternatives, for example:

  • a combination of classes 1.1 and public property declarations
  • a combination of private x syntax for soft privacy and private symbols for hard privacy

(I can imagine a rationale document that would make a strong case for the current proposal over these alternatives. It wouldn't satisfy everyone of course, but I think it would help explain the decisions in a way that the meeting notes currently don't do adequately.)

@littledan
Copy link
Member

I meant, we should make such comparison/rationale documents earlier in the process of future proposals. For proposals at later stages of development, like this one, an FAQ explaining the selected option is probably the more relevant thing.

If anyone wants to write either form of documentation for this proposal, I would be happy to review it and include it in this repository.

Right now, I'm most excited about @neilkakkar 's effort to document this proposal in MDN, and other efforts to help JavaScript developers use the feature, rather than explain the rationale.

@mbrowne
Copy link
Author

mbrowne commented Dec 7, 2018

For proposals at later stages of development, like this one, an FAQ explaining the selected option is probably the more relevant thing.

Ok, it could be in an FAQ format; the important thing is "explaining the selected option". But I don't see how a non-committee member could write the first draft...plenty of details can be gleaned from public statements by committee members, but the big picture...? (For the syntax, the private fields FAQ would be an excellent starting point, but the syntax is only one aspect.) Maybe a gist from the committee with some bullet points could provide the starting point, and community members who are familiar with the issues (hard vs soft privacy, Set vs Define, etc.) could fill in the details including a glossary of terms.

Right now, I'm most excited about @neilkakkar 's effort to document this proposal in MDN, and other efforts to help JavaScript developers use the feature, rather than explain the rationale.

Given that the committee has already reached consensus on this proposal, it does make sense for this to be the first priority, yes.

@littledan
Copy link
Member

You're right, it doesn't need to be in FAQ form. Happy to answer any questions you have; I would recommend starting with the past presentations and notes linked from the readme if you are interested in history beyond the rationale we have been discussing in these threads.

@neilkakkar
Copy link

Yes, it's a good idea to ask questions. If I know about FAQs beforehand, I can pre-emptively include them in the documentation.

@mbrowne
Copy link
Author

mbrowne commented Dec 8, 2018

I thought @littledan's most recent comment was in reference to the rationale for choosing this proposal over alternatives. As to usage, personally I'm pretty clear on it. But I do have an idea for a usage FAQ question...

Question: How do I access fields defined in a subclass from a parent class's constructor?
Answer: This is intentionally not possible. [Then explain how internally, fields aren't defined until after calling super.]

@littledan
Copy link
Member

Happy to discuss both kinds of questions.

@mbrowne
Copy link
Author

mbrowne commented Dec 8, 2018

Thank you @littledan! :) I started a new thread for this:
#178

@MichaelTheriot
Copy link

Catching up on this long thread. Were non-React use cases for public fields ever posted? They still seem redundant to me if you are defining a constructor, and if you are not defining a constructor then using a class over a plain object seems unusual. Would like to understand more.

@littledan
Copy link
Member

@MichaelTheriot Yes, for example, see the example in the explainer of this repository, which is based on custom elements and does not use React.

@MichaelTheriot
Copy link

If we are talking about the same example it appears to suffer from the same issues I mentioned.

@mbrowne
Copy link
Author

mbrowne commented Jan 5, 2019

@MichaelTheriot for a recent, detailed discussion of this issue, see rdking/proposal-class-members#1 and the continuation of that discussion in rdking/proposal-class-members#3

@mbrowne
Copy link
Author

mbrowne commented Jan 5, 2019

Also #142 (comment), and #100 (comment)

@ljharb
Copy link
Member

ljharb commented Jan 5, 2019

If you’re not defining a constructor but are inheriting from another class - one that has an API that reads from instance properties - then you couldn’t use a plain object (or instanceof would break).

React is just one example of this pattern; Backbone is another, and I’m sure there’s many current ones too that i don’t have available off the top of my head.

@trusktr
Copy link

trusktr commented Feb 6, 2019

Sidenote, as an end user of "open standard" JS and Web APIs, I feel that our developer experience may not always valued enough. The discontent summarized in #100 is another example.

@rdking
Copy link

rdking commented Feb 7, 2019

@ljharb

If you’re not defining a constructor but are inheriting from another class - one that has an API that reads from instance properties - then you couldn’t use a plain object (or instanceof would break).

Not true, since that plain object can just do:

let myobj = {
  somefields: somevalue,
  __proto__: ExpectedClass.prototype
};

This is still a fundamentally prototype-based language.

@ljharb
Copy link
Member

ljharb commented Feb 7, 2019

@rdking you’re correct of course, but “plain object” tends to mean one that inherits directly from Object.prototype; you’re describing an object literal.

@rdking
Copy link

rdking commented Feb 7, 2019

Fair enough. I was thinking of "plain object" in terms of an object not created by any kind of factory function or class.

@Mouvedia
Copy link

It would be unexpected and weird if "private" things were publicly visible properties that threw on access, exposing their existence.

Assuming that private properties are not enumerable, if non-public—even non existing ones—properties throw, it virtually seals the class—i.e. preventing any additions.
Is not allowing additions (which would allow private properties detections) be such a big concession?
That way you won't be able to tell if a private property exists by trying to access it.

@ljharb
Copy link
Member

ljharb commented Mar 22, 2019

Yes, it would be an unacceptable concession; JS objects are extensible by default, and making "adding the first private field" be the equivalent of adding Object.preventExtensions(this) at the end of the constructor, or making "removing the last private field" the equivalent of making the object suddenly and surprisingly extensible, would be a very confusing situation for the language to find itself in.

@Mouvedia
Copy link

Mouvedia commented Mar 22, 2019

@ljharb of course that would be accompanied with a way to reflect that state (a property boolean or predicate function).

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