Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Summary: Objections to fields (as opposed to alternatives), especially the private field syntax #150

Open
mbrowne opened this issue Oct 17, 2018 · 136 comments

Comments

@mbrowne
Copy link

@mbrowne mbrowne commented Oct 17, 2018

Continuing from #100 and other threads, here's a summary of key objections to this proposal raised by the community:

@hax's top 3 concerns:

  1. TC39 underated the risk of community break which is very harmful to all of us.
  2. TC39 current process failed on such controversial problems.
  3. Some TC39 members use a vicious circle and/or double standards to dismiss the issues/alternatives other raised/offered.

Also, he believes another long wait for private state is acceptable if that's what it takes to get it right: the problems with the current proposal—including with public fields—are just too severe.

Regarding the substance of the proposal:

  • Duality of private vs. public fields will cause confusion: similar syntax but very different semantics
  • # syntax does not fit into the mental model of JS devs and will cause a community split, and TypeScript issues will further fracture the community
  • We don't need public property/field declarations and if we must have them, they should follow existing property semantics instead of fields. Fields cause pernicious bugs when overriding properties in subclasses or refactoring base classes.
  • Classes 1.1 proposal offers a better alternative in many ways, including instance variable semantics

@rdking:

  • Similar concerns about the process as @hax. Difficult to understand the committee's requirements and the reasons for them.
  • "Undetectability" should not be a requirement since it falls short of a fully protective security feature
  • Strongly believes that the best solution will integrate with existing features instead of crippling or disabling them, e.g. inconsistency of obj['property'] for public fields but no equivalent for private fields breaks existing feature by breaking expectations. Lack of dynamic access of private fields (e.g. this['#x']) is a deal-breaker.
  • # is not "well-defined"—has two meanings. Context is insufficient to disambiguate declaration and access.
  • Advocates property semantics instead of field semantics (but somewhat differently from @hax; see #144)

@bdistin:

  • This proposal lacks long-term planning and will irreversibly reduce the design space for important future proposals such as keywords for other access modifiers
  • Agrees with @rdking that lack of dynamic private properties is a deal-breaker; violates core strength of the JS language (dynamicism)

@mbrowne (me):

(NB: it's mbrowne, not mbrown)

  • This is a solid proposal as-is, but agree with @bdistin's point about long-term planning
  • Strongly agree with @hax that declarations should use property semantics, specifically the part about using [[Set]] instead of [[CreateDataPropertyOrThrow]], for the reasons described in #151 (comment) and #144
  • private #x syntax should be strongly considered because # alone might make future native support of intermediate access levels impossible. But # alone might be OK if we are confident we could have another way of natively sharing private fields within a module.

@Igmat:

  1. agree with @hax about somewhat failed process for such controversial topic
  2. brand-checking shouldn't be added implicitly to all private reads/writes, since it breaks metaprogramming with proxies as described in #106 and #134
  3. strongly agree with @hax about that [[Set]] should be used instead of [[CreateDataPropertyOrThrow]]
  4. agree with that current proposal should allow future native expansion in some way (there are a lot of ways to achieve that)
  5. Symbol.private + some kind of shorthand syntax for making symbols more ergonomic, provides much cleaner mechanism for encapsulation

@shannon:

  1. # being part of the name leads to confusion because this.#x !== this['#x'], it's just too weird. The sigil is fine but the functionality is just too different.
  2. No dynamic access, no destructuring is a deal breaker for me
  3. I agree with @Igmat that "brand-checking shouldn't be added implicitly to all private reads/writes"
  4. I agree with @Igmat that "proposal should allow future native expansion in some way (there are a lot of ways to achieve that)"
  5. I agree with @rdking that "the best solution will integrate with existing features instead of crippling or disabling them."
  6. I believe a private symbol approach (as described in syntax 1 here #149) is by far the simplest addition to existing JS paradigms/existing JS class spec, and achieves most of what the current proposal wants with a few minor exceptions (possibility to leak private symbols for one, but I think this could actual be useful for things like protected to be implemented in userland).

@aimingoo:

  • Concept "Fields" is unclear and its necessity as an independent concept is questionable. The concept undermines the "Object is collection properties" core concept, and the impact will be extremely far-reaching, but nobody taken seriously. The Similar sound includes: Not Fields in Classes 1.1 proposal.
  • Using the lexical environment to implement "private field" is to recreate var and ctx's VariableEnvironment, which is an abuse of Environment components, and it is obviously too bulky.
  • The syntax is very unfriendly, especially with # as a prefix character for declaring fields and for name operations (such as the semantics of this["#x"] vs. this.#x). About grammar, I recommend enabling the private keyword to declare private properties (Note: that I don't support new concepts like "Fields or Private Fields"). And # as an operator, this is reasonable in both this#data and #data as unary or binary operators.

Reference: #148 , #100 (comment)


@RyanCavanaugh:

  • The choice of [[Define]] over [[Set]] for initializers is a bad trade-off that throws away six years of evidence from TypeScript and Babel developers' use of fields. The "natural experiment" has been performed, the results are clear, and the runtime breakage incurred here is very difficult to detect with tooling. The proposed upside of [[Define]] (const fields later) is purely speculative; the downside of not being able to install a deprecation setter is concrete and immediate.
  • Making initializers on fields optional, especially combined with the above, is unintuitive and dangerous. This syntactic expansion isn't strictly necessary and represents a substantial breaking change problem for TypeScript/Flow/Babel with no clear upside.
  • Honestly, the negative response to the # sigil is something we should just ignore and take the hit for, if built-in hard privacy is really needed. People will adapt and the runtime need for it is extremely clear; all counterproposals have been ignorant of the design constraints or runtime semantics.

@Jamesernator

my main objection to the current private fields proposal (and more so private methods) is that it throws away the notion of prototypal inheritance in favour of an object branding mechanism inconsistent with how other properties work.


In addition, some members of the TC39 committee also have current objections to this proposal. For example:

@allenwb:

See the Classes 1.1 motivation document. In particular if you negate the descriptions in the Design Rules bullet list you have a good summary of my concerns with the major problems being:

Design Rules:
Item 2:

Be as small as possible

Item 3:

Support a user conceptual model that is as simple as possible. It should be easy to teach and easy to understand.

Item 4:

Be internally consistent, with few, if any, special cases or unexpected feature interactions.


@zenparsing:

I believe that we are overpricing the benefit of this entire feature set while underpricing the risk.

Why do I think that we're overpricing the benefit?

  • In my experience, most of our constituency does not need hard-private in their day-to-day programming work. Hard private is important for a small number of use cases, such as platform library code and high-profile libraries which are especially subject to breaking consumers when implementation details are updated. Most application programmers I know don't write that kind of code. Sorry 🤷‍♀️
  • Hard privacy can already be achieved with decent ergonomics. See https://github.com/zenparsing/hidden-state#example, for example.
  • Folks that really want "fields" are mostly happy using TypeScript already. It doesn't seem implausible to me that TS could have an option for generating hard-private code for private x, perhaps with some optimization work applied to WeakMap on the engine side.

Why do I think that we're underpricing the risk?

  • We know that many devs don't like the syntax, and don't understand why the syntax that they enjoy using (private x from TypeScript) won't work. It doesn't "just work". We are taking on the risk that this feature will continue to create head-scratching across our community.
  • We're taking on the risk that we'll want # for something else and it won't be available.
  • We're taking on a small amount of ASI risk.
  • We're taking on a risk that by pushing something unpopular our community will lose some amount of faith in TC39 and the future of JS.

I probably missed something important, and obviously I haven't included a summary for everyone who has commented. If you feel something is missing, please provide a concise summary of your feedback as your first comment on this thread (saving longer arguments for subsequent comments). Also feel free to simply say, "I agree with [username]".

@Igmat
Copy link

@Igmat Igmat commented Oct 17, 2018

@Igmat (me):

  1. agree with @hax about somewhat failed process for such controversial topic
  2. brand-checking shouldn't be added implicitly to all private reads/writes, since it breaks metaprogramming with proxies as described in #106 and #134
  3. strongly agree with @hax about that [[Set]] should be used instead of [[CreateDataPropertyOrThrow]]
  4. agree with that current proposal should allow future native expansion in some way (there are a lot of ways to achieve that)
  5. Symbol.private + some kind of shorthand syntax for making symbols more ergonomic, provides much cleaner mechanism for encapsulation
@rdking
Copy link

@rdking rdking commented Oct 17, 2018

After talks with @ljharb, I concede that "undetectability" is a reasonable desire. Whether that reaches the level of a requirement depends on whether or not achieving it causes some existing functionality to fail or work in unexpected ways.

I don't remember wanting the ability to dynamically add a private field. I feel that would be counter-productive and dangerous.

I've also been working with the advocates of the classes-1.1 proposal on issues of functionality and implementation. You can count me as being in favor of that proposal.

@mbrowne
Copy link
Author

@mbrowne mbrowne commented Oct 17, 2018

Sorry @rdking, that must have been a bad recollection on my part. I removed that from the original post.

@hax
Copy link
Member

@hax hax commented Oct 17, 2018

I don't remember wanting the ability to dynamically add a private field.

Someone want it. But I can't find the comment, too many 🤣

@rdking
Copy link

@rdking rdking commented Oct 17, 2018

  • strongly agree with @hax that [[Set]] is more consistent with current behavior than [[CreateDataPropertyOrThrow]].
  • believes that public properties should be getter/setter accessors backed by private properties
  • believes that the need for other accessor types will become significantly more apparent after the release of a private property solution.
  • strongly believes that the best solution will integrate with existing features instead of crippling or disabling them.
@rdking
Copy link

@rdking rdking commented Oct 17, 2018

@hax I remember that too, from when I debuted the idea behind proposal-object-members. Someone thought that would enable the possibility for dynamically adding private fields (and indeed it could), but I said that wasn't part of the plan.

@bdistin
Copy link

@bdistin bdistin commented Oct 17, 2018

@mbrowne responded to your clarification request here

@bakkot
Copy link
Contributor

@bakkot bakkot commented Oct 17, 2018

@mbrowne re: "dynamic private properties", I assume you're thinking of @shannon here.

@shannon
Copy link

@shannon shannon commented Oct 17, 2018

I don't mind so much that they can't be created dynamically after construction. I just think dynamic access is a staple of JS and not having it is a huge oversight. Right now private symbols seems the best approach to achieve this in my opinion.

@shannon
Copy link

@shannon shannon commented Oct 17, 2018

Summary of my feelings of this proposal

  1. # being part of the name leads to confusion because this.#x !== this['#x'], it's just too weird. The sigil is fine but the functionality is just too different.
  2. No dynamic access, no destructuring is a deal breaker for me
  3. I agree with @Igmat that "brand-checking shouldn't be added implicitly to all private reads/writes"
  4. I agree with @Igmat that "proposal should allow future native expansion in some way (there are a lot of ways to achieve that)"
  5. I agree with @rdking that "the best solution will integrate with existing features instead of crippling or disabling them."
  6. I believe a private symbol approach (as described in syntax 1 here #149) is by far the simplest addition to existing JS paradigms/existing JS class spec, and achieves most of what the current proposal wants with a few minor exceptions (possibility to leak private symbols for one, but I think this could actual be useful for things like protected to be implemented in userland).
@nicolo-ribaudo
Copy link
Member

@nicolo-ribaudo nicolo-ribaudo commented Oct 17, 2018

@mbrowne Could you please add @shannon and @Igmat's feedback to the issue description? Just to avoid loosing them in thousand of comments like in the other issues.

@mbrowne
Copy link
Author

@mbrowne mbrowne commented Oct 17, 2018

@nicolo-ribaudo Sure - updated. I also added @allenwb's feedback.

@rdking I am trying not to add too many bullet points of closely related items or second tier concerns, but this one that you added seems particularly significant:

strongly believes that the best solution will integrate with existing features instead of crippling or disabling them.

Can you expand on that slightly, while still keeping it a concise summary? I'd like to add it to the issue description but it doesn't say what you're specifically referring to.

Or if you prefer that I completely replace your summary, you can post something for me to copy and paste.

@mbrowne
Copy link
Author

@mbrowne mbrowne commented Oct 17, 2018

@rdking
Copy link

@rdking rdking commented Oct 17, 2018

@mbrowne The things I listed were in addition to what you've already got.

strongly believes that the best solution will integrate with existing features instead of crippling or disabling them.

Where this is concerned, I mean that, using the concept of a "private property", since this is just another property of an object, it is reasonable for the uninitialized to assume that access capability will be no different for public than it is for private, especially since they both take the same form. So with the existing feature being

  • I can access a property with either obj.<property> or obj['property']
    the existence of a private property should not break this expectation.

This is even more true in the case of Proxy where the completely undetectable private properties are not being stored in a separate object that uses the identity of the owner as a lookup reference, but somehow, Proxy still throws when dealing with a method accessing private fields. While the reason it throws is directly analogous to WeakMaps (the Proxy object itself is passed as the "target" to the handler functions), there's no reason for this to be the case, especially considering that developers implementing private fields in their libraries will be flooded with feedback from their users about use cases where whatever they used a Proxy for is now breaking merely due to the presence of the private fields.

Put shortly: If the proper introduction of a new feature into existing, working code bases that don't rely on the improper use of properties not intended for external use, causes the code to break, then the new feature has broken an existing feature.

@zenparsing
Copy link
Member

@zenparsing zenparsing commented Oct 17, 2018

Since you asked...

I believe that we are overpricing the benefit of this entire feature set while underpricing the risk.

Why do I think that we're overpricing the benefit?

  • In my experience, most of our constituency does not need hard-private in their day-to-day programming work. Hard private is important for a small number of use cases, such as platform library code and high-profile libraries which are especially subject to breaking consumers when implementation details are updated. Most application programmers I know don't write that kind of code. Sorry 🤷‍♀️
  • Hard privacy can already be achieved with decent ergonomics. See https://github.com/zenparsing/hidden-state#example, for example.
  • Folks that really want "fields" are mostly happy using TypeScript already. It doesn't seem implausible to me that TS could have an option for generating hard-private code for private x, perhaps with some optimization work applied to WeakMap on the engine side.

Why do I think that we're underpricing the risk?

  • We know that many devs don't like the syntax, and don't understand why the syntax that they enjoy using (private x from TypeScript) won't work. It doesn't "just work". We are taking on the risk that this feature will continue to create head-scratching across our community.
  • We're taking on the risk that we'll want # for something else and it won't be available.
  • We're taking on a small amount of ASI risk.
  • We're taking on a risk that by pushing something unpopular our community will lose some amount of faith in TC39 and the future of JS.

Thanks for asking me to share my thoughts (I'm not particularly interested in debating though).

@littledan
Copy link
Member

@littledan littledan commented Oct 17, 2018

@mbrowne Thanks for the summary! This helps a lot.

@aimingoo
Copy link

@aimingoo aimingoo commented Oct 17, 2018

Summary of my comment/issues:

  • Concept "Fields" is unclear and its necessity as an independent concept is questionable. The concept undermines the "Object is collection of properties" core concept, and the impact will be extremely far-reaching, but nobody taken seriously. The similar sound includes: Not Fields in Classes 1.1 proposal.

  • Using the lexical environment to implement "private field" is to recreate var and ctx's VariableEnvironment, which is an abuse of Environment components, and it is obviously too bulky.

  • The syntax is very unfriendly, especially with # as a prefix character for declaring fields and for name operations (such as the semantics of this["#x"] vs. this.#x). About grammar, I recommend enabling the private keyword to declare private properties (Note: that I don't support new concepts like "Fields or Private Fields"). And # as an operator, this is reasonable in both this#data and #data as unary or binary operators.

Reference: #148 , #100 (comment)

@mbrowne
Copy link
Author

@mbrowne mbrowne commented Oct 17, 2018

Updated. @zenparsing and @BrendanEich, I mentioned you not to encourage you to join the debate (unless you want to), but just to give you an opportunity to have your feedback included in the issue description. Thank you everyone for summarizing your feedback.

@RyanCavanaugh
Copy link

@RyanCavanaugh RyanCavanaugh commented Oct 18, 2018

I will summarize my own feedback as well:

  • The choice of [[Define]] over [[Set]] for initializers is a bad trade-off that throws away six years of evidence from TypeScript and Babel developers' use of fields. The "natural experiment" has been performed, the results are clear, and the runtime breakage incurred here is very difficult to detect with tooling. The proposed upside of [[Define]] (const fields later) is purely speculative; the downside of not being able to install a deprecation setter is concrete and immediate.
  • Making initializers on fields optional, especially combined with the above, is unintuitive and dangerous. This syntactic expansion isn't strictly necessary and represents a substantial breaking change problem for TypeScript/Flow/Babel with no clear upside.
  • Honestly, the negative response to the # sigil is something we should just ignore and take the hit for, if built-in hard privacy is really needed. People will adapt and the runtime need for it is extremely clear; all counterproposals have been ignorant of the design constraints or runtime semantics.
@mbrowne
Copy link
Author

@mbrowne mbrowne commented Oct 18, 2018

@RyanCavanaugh

Making initializers on fields optional, especially combined with the above, is unintuitive and dangerous. This syntactic expansion isn't strictly necessary and represents a substantial breaking change problem for TypeScript/Flow/Babel with no clear upside.

How is it a breaking change or problem for Babel? Or do you just mean when using Babel and Flow together? I think undefined is a good default and don't see the advantage of explicitly initializing propertyName = undefined considering that this would be very common.

@RyanCavanaugh
Copy link

@RyanCavanaugh RyanCavanaugh commented Oct 18, 2018

@mbrowne
Link

class Base {
  prop = 10;
}

class Derived extends Base {
  prop;
}

// Prints 10 in Babel 6.26, would print 'undefined'
console.log(new Derived().prop);
@mbrowne
Copy link
Author

@mbrowne mbrowne commented Oct 18, 2018

This would be so much simpler and avoid many of the issues raised if redeclaring the same property in a subclass were simply illegal...you can already set different initial values if you want (or even call Object.defineProperty) in the constructor.

@rdking
Copy link

@rdking rdking commented Oct 18, 2018

@mbrowne Since properties are either data properties or accessor properties, it might be better if redeclaration of a data property that doesn't changing the property type from data to accessor is illegal. Likewise, changing from accessor to data should also be illegal. Redefining accessor properties as new accessor properties should be allowed as this might represent some useful change to the accessors.

@rdking
Copy link

@rdking rdking commented Oct 18, 2018

@RyanCavanaugh

The proposed upside of [[Define]] (const fields later) is purely speculative;

...and irrelevant since even with [[Set]], the action can be immediately followed by a change to the writability of the property, leaving all side effects of setting the value in tact.

@bakkot
Copy link
Contributor

@bakkot bakkot commented Oct 18, 2018

If possible, can I suggest we keep discussion of specific points to other threads? (Anything beyond clarifications, anyway.) I'd like to keep this thread as an accessible summary.

@rdking
Copy link

@rdking rdking commented Oct 18, 2018

@RyanCavanaugh

Honestly, the negative response to the # sigil is something we should just ignore and take the hit for, if built-in hard privacy is really needed. People will adapt and the runtime need for it is extremely clear; all counterproposals have been ignorant of the design constraints or runtime semantics.

  1. Kinda difficult to be aware of design constraints that are never revealed.
  2. The runtime semantics of the current proposal is crippling enough to break a sizable portion of the code in the wild should library authors decide to rewrite making use of this proposal's version of private fields.
  3. While the counterproposals do take into account different constraints, the general goal of adding private in such a way that if the only changes made are to make private those properties meant to be implementation details, then no code that did not make ill-advised use of such fields will be broken, is the one thing they all have in common, while differing with the current proposal.
  4. ES does not need built-in hard privacy. It's just an extremely strong want. Hard privacy is already well provided for via judicious manipulation of closures and WeakMap. I would much rather see the TC39 take this proposal back to stage 1 or 2 so it can be re-formulated than see it implemented, and watch as people are frustrated by unnecessary code-bloat (due to lack of [] support for private fields), broken libraries (due to incompatibility between Proxy and private fields), disabled functionality (due to field definition being a [[Define]] instead of a [[Set]] causing prototype properties to be ignored unexpectedly), and other such issues.
@hax
Copy link
Member

@hax hax commented Oct 19, 2018

@RyanCavanaugh

  • Honestly, the negative response to the # sigil is something we should just ignore and take the hit for, if built-in hard privacy is really needed. People will adapt and the runtime need for it is extremely clear; all counterproposals have been ignorant of the design constraints or runtime semantics.

Even hard privacy is must, both classes 1.1 and Symbol.private proposal meet this requirement. Basically instance variable in classes 1.1 keep the same semantic of current private field, but use different syntax which avoid controversial #. The other big difference between classes 1.1 with current proposal is classes 1.1 doesn't include public field, but it seems you agree current semantic of public field has many problems.

@hax
Copy link
Member

@hax hax commented Apr 16, 2019

@mbrowne

I don't think this is the best place to have a debate about the purpose of classes.

Sorry I still want to add a small comment about "the purpose of classes" (hope I could say it clear in one time so I don't need to add more.)

Whether we like or not, ES6 classes already have inheritance and accessors. Some guys don't like inheritance or assessors and advocate not use them at all, so it's understandable why they choose to ignore the [[Define]] issue. But I believe it's very wrong to force others follow your way by breaking existing features.

More important, ES6 classes have very similar code to other mainstream languages, also very similar high-level OO concept model to other mainstream languages, even it's prototype based, which is very different to all other languages.

So a reasonable assumption is, "the purpose of ES6 classes" is to match the developer experience of classes in other mainstream languages. Note, some parts (encapsulation, traits/mixins, etc.) were missing because TC39 do not have enough time to get consensus about them in ES6 timeline. But the purpose is clear, at least in that time.

It's obvious to me current proposal not follow such purpose anymore. Whether it's ok or not, may be a separate problem.

@mbrowne
Copy link
Author

@mbrowne mbrowne commented Apr 24, 2019

FYI, private fields have shipped in Chrome.

@ljharb
Copy link
Member

@ljharb ljharb commented Apr 24, 2019

also in node v12.

@brianjenkins94
Copy link

@brianjenkins94 brianjenkins94 commented May 15, 2019

Hi.

I'm late to the party here, but I have two concerns:

  1. Doesn't "hard-private" severely undercut the ability to polyfill and monkey-patch the language? Isn't that one of the primary reasons why JavaScript has been able to persist as the language of the web?

  2. Private class fields could encourage developers to think that their client-side code is safe, and as a result, trust the client. Keeping in mind that we should never trust the client, what do we stand to gain here that we didn't already have with "soft-private"? Why is it so important that methods like getOwnPropertySymbols not reveal private fields?

Let me know if these types of concerns have been expressed and addressed elsewhere.

Edit: I read this but I didn't find it very compelling. If "hard-private" is already polyfillable this feels more like an opportunity for a framework to simplify its usage, or a babel plugin if this is really that important to you.

I really don't want to lose the ability to see all of the properties in an object, and I really don't think it's wise to let developers let their guard down just because they're in a private scope.

@ljharb
Copy link
Member

@ljharb ljharb commented May 16, 2019

@brianjenkins94

  1. no, no more than closed-over variables prevents monkey-patching of functions.
  2. it’s not about trust; again, if closed-over variables don’t cause this problem, then private class fields will not either.
@rdking
Copy link

@rdking rdking commented May 16, 2019

@brianjenkins94
When you think about it, there are already plenty of JS object that don't allow you to see all of their properties. They're all built-ins but that doesn't change the reality. Compound this with the fact that people already use closures to hide things owned by objects & ES5-style classes, and you're left with the reality that even without this proposal, you already can't see all the properties in an object.

Though I have many reasons to not like the private-fields implementation, the concerns you mentioned are not among them.

@brianjenkins94
Copy link

@brianjenkins94 brianjenkins94 commented May 16, 2019

That's a relief. Thank you guys for the confirmation.

@tiandian
Copy link

@tiandian tiandian commented May 18, 2019

I agree with @hax,

Since javascript has a directive "use strict" defines that JavaScript code should be executed in "strict mode".

Maybe javascript can add a new directive such as "use strict c#" or "use strict java" to defines that javascript class member's grammar declared respect the rule of java(c#) class.

@rdking
Copy link

@rdking rdking commented May 20, 2019

@tiandian A couple of problems with that.

  1. While Java/C#-style classes are what most class-using developers wanted to see, that approach to classes wasn't really appropriate for ES (albeit, that wouldn't have been any worse than what we were handed instead). Private-fields are already an attempt to provide encapsulation in a way similar to those other languages, and look at the problems it brings.
  2. Engine developers suffered so many regrets over this "use strict" directive that they've decided it's never to be repeated again. No engine developers are willing to add any new directives to ES. They say it adds excessive complexity to the engine.
Off Topic Comment
If any directive could still be added, I'd want a `"use sloppy"` directive to turn off strict-mode, even in cases where it was automatically turned on.
@bakkot
Copy link
Contributor

@bakkot bakkot commented May 20, 2019

@rdking I don't think engine developers regret "use strict". It's very valuable. You are correct that they are not willing to add another new mode, but the first one was worth adding.

@trusktr
Copy link

@trusktr trusktr commented May 20, 2019

I don't think this is the best place to have a debate about the purpose of classes. This thread is a summary of community feedback to the committee, and should remain focused on that or else it will become nothing more than a chat room

@mbrowne That makes sense and I agree

But you've gotta admit: it is hard not to want to leave a reply like my previous one when a committee member has argued for a feature using points such as the one I disproved.

(I didn't provide actual examples, but if you happen to know poor-man's OOP in C (f.e. GLib), or just pure functional languages, then you won't need examples to know how my reasoning disproves what he said.)

Classes are about inheritance.

Even a "non-inheriting base class" in the sense that @ljharb speaks of, is still about inheritance, because it can be inherited. (In practice next to no one will ever write class Foo extends null {}, though keep in mind it can still be inherited.)

@ljharb Why are you so interested in the design of class features if you don't believe in how closely class relates to inheritance?

Let's please take inheritance into consideration when designing new class features.

Sorry, I'll avoid commenting off-topic here now.

@slikts
Copy link

@slikts slikts commented May 21, 2019

"It can be used for inheritance therefore it's all about inheritance" is not any kind of a water-tight point.

@mbrowne
Copy link
Author

@mbrowne mbrowne commented May 21, 2019

@trusktr I think there must be a lot of comments from committee members that you missed over the past few years if you think they were glossing over inheritance use cases in the design of this proposal. There are certain things like the absence of protected that might make it look like inheritance was de-prioritized, but actually those decisions were usually made for other reasons and I think an effort was made to support inheritance as well as possible, while also meeting the other design goals.

@rdking
Copy link

@rdking rdking commented May 21, 2019

I think this whole line of conversation has gone too far off topic.
That being said....

@mbrowne
It's nice that they have "other design goals" that they can use to justify their design decisions. The problem is that even after re-designing the FAQ, they still never exposed their design goals, only their choices, and the justifications thereof. Admittedly, in the technical cases, that's usually good enough. However, when the decision is predicated on a mere preference, an understanding of the design goal behind that preference is needed in order to understand why one choice is preferred over another that seems logically and technically superior.

When it comes down to it, the implementation of fields hampers the pre-existing use cases for inheritance. Where fields are present, various other inheritance features will not work as in the past or as expected. It's not as if there are not alternate possible implementations that produce semantically equivalent results without the unnecessary complications and counter-intuitive side effects. But once again, the undocumented design goals have stood as reasons why such implementations were never given much if any consideration.

Like you, I want to give TC39 as much credit as they deserve for coming as far as they have with this proposal. However, I also believe they need to be open to receiving much criticism for pushing forward with such an enticing, yet feature and design pattern interfering and hampering proposal.

@c69
Copy link

@c69 c69 commented Jun 21, 2019

only yesterday i realized that discussion moved to this new repo (8 months ago). #slowpoke is slow

if anyone still cares, here is my summary:

  • oop is of questionable value today (Entity-Component-Systems in game dev, data-oriented approaches, STL, fp fundamentalists, ect)

  • incapsulation is of questionable value to most of JS programmers

  • i dont use classes in my own code (preferring functions and modules)

  • typescript does not use classes in current version of compiler

  • react is moving to functional components, angular is compiling everything to functions

  • now: There are classes in ES6. They provide familiar syntax to developers coming from Java/C#.

  • Typescript has short syntax for class public fields.

  • its worth standardizing it, as defacto standard.

  • hard private via WeakMaps is achieving hard private. But its meaning is not "private" in a common sense. Its "privately shared" and also "not inherited".

  • the need for sigil is caused by banal name resolution (namespacing).

  • if in future new scope / visibility modifiers will be added - they will face same issue (but # will be taken already)


TL;DR

  • "classic oop" is JS is meant to simplify entry barrier for people with OOP background. Sigil will add confusion.
  • "library authors suffering from people using private methods" is a strawman. Most of the popular libraries are not even using ES6 classes.
  • # sigil is taking one of the two remaining typeable-on-keyboard chars and wasting it for extremely narrow use case is just inviting regrets few years down the road (because nothing can be removed from JS, see with).
  • there are already more than a few accepted proposals that nobody uses because they are broken (Proxies, Tail Recursion) or not implemented (Regexp named groups, lookbehind), or just have poor ergonomics (BigInt, Set, Map).
  • public class fields (typescript/babel) are de-facto standard, the focus should be on working them into a language. So it makes sense to split them and move on. I have no position on [[Define]] vs [[Set]] discussion.
@trusktr
Copy link

@trusktr trusktr commented Jun 26, 2019

@rdking always explains everything the best, and always makes sense!

When it comes down to it, the implementation of fields hampers the pre-existing use cases for inheritance. Where fields are present, various other inheritance features will not work as in the past or as expected.

It is breaking two decades worth of JavaScript paradigms.

@trusktr
Copy link

@trusktr trusktr commented Jun 26, 2019

Just wait until some says

"Cool, I can move these properties out of the constructor and remove the constructor definition to shorten my code.... Wait, what the hell? Why doesn't it work now? Damn, what's this [Define]] nonsense?".

Authors of this spec seem to be fine with breaking decades worth of expectations.

@trusktr
Copy link

@trusktr trusktr commented Jun 26, 2019

Has anyone here posted objections to class fields in the issue trackers of browsers that are implementing class fields? Maybe that could help.

I started with two:

@ljharb
Copy link
Member

@ljharb ljharb commented Jun 26, 2019

The response on both of those was "we are not interested in deviating from what TC39 has decided". Considering that representatives of all the browsers were part of this proposal's consensus, it doesn't seem a viable course of action.

@rdking
Copy link

@rdking rdking commented Jun 28, 2019

While the may not be "interested in deviating", that does not mean that if enough issues are raised, they wouldn't be willing to consider it necessary to remove the implementation.

@bakkot
Copy link
Contributor

@bakkot bakkot commented Jun 28, 2019

All of the major vendors participate in TC39 already. Please do not spam their issue trackers and waste the time of their triage people.

@rdking
Copy link

@rdking rdking commented Jun 28, 2019

@bakkot I understand the nuisance of such developer useless spam all too well. I don't want to see that happen either. So that raises a very direct question: How does the community at large go about getting TC39 members to recognize and accept that this proposal in its current form is introducing unacceptable issues? What will it take? Or is it TC39's stance that there is no recourse for the community should it reject use of the proposal?

@bakkot
Copy link
Contributor

@bakkot bakkot commented Jun 28, 2019

@rdking The process for you to get TC39 to agree with your position is for you to convince TC39 of your position on this issue tracker. If there are issues which we have not already discussed to death, feel free to bring them up. (As I've said several times, repeating issues we've already discussed at great length is unlikely to convince anyone of anything.)

If you can't convince TC39 of your position here, spamming browser vendors elsewhere is not going to help anything.

@rdking
Copy link

@rdking rdking commented Jun 28, 2019

Let's go to a different thread, because I have a few more questions.... #253

@iugo
Copy link

@iugo iugo commented Oct 15, 2019

I agree with @hax

@Mouvedia
Copy link

@Mouvedia Mouvedia commented Jun 25, 2020

As @eddyw pointed out already, until 2012 # was reserved for sharp variables on Firefox—which had nothing to do with privates.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet