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

Should static public fields be coupled with static private fields and methods? #28

Closed
littledan opened this issue Apr 12, 2018 · 11 comments

Comments

@littledan
Copy link
Member

In the March 2018 TC39 meeting, I argued for decoupling static public fields from static private fields and methods, but we heard some skepticism from some committee members about this plan. Some sources of this skepticism:

  • The subclassing "hazard": This pertains to the "hazard" of getting a TypeError when reading a public static field or method using a subclass receiver, vs the "hazard" of seeing object Set semantics when writing to a public static field. My feeling is that the Set "hazard" is really not a problem (just normal object semantics), though I can understand why some people believe the static private hazard is bad. @waldemarhorwat claimed otherwise, that they are equally bad, and that we need to make a common decision on both of them.
  • Orthogonality: For consistency, @erights claimed that it's essential to "fill out the grid" and have static private if we have static public and instance private. This differs from the previous orthogonal classes framework which framed the design space as a grid with holes. I'm a supporter of the "grid with holes" phrasing--syntax just isn't always 100% orthogonal in general, e.g., in classes, we don't have async getter methods; we have to take things case-by-case and pragmatically sometimes.
  • Utility: @rbuckton, @jridgewell, @domenic and @bakkot have made the case for the utility of private static fields and methods eventually being added (whether it is in this proposal or a later one).

Can we eliminate the private static subclassing hazard?

If we add private static fields and methods, I think the answer is no. We've investigated several alternatives in this repository, and none seem feasible.

In individual discussions with people who raised the subclassing hazard, who are also some of the strongest proponents of static private fields and methods, I heard a common response that the original, "hazard" semantics are acceptable if this is the only feasible alternative, especially in conjunction with a shorthand syntax for accessing properties of the constructor.

What about the other follow-on proposals?

Many committee members are strongly in favor of additional follow-on proposals documented in this repository, including static blocks in classes and lexically scoped private names outside of classes. Static private fields and methods serve some, but not all, of the same use cases as these other features.

If we advance static private fields and methods, we may want to think through the following propositions about how this would affect how those other features are taken:

  1. "We are comfortable with having both static private fields/methods as well as either (or both) of those other two features."
  2. "We knowingly accept the risk of later deciding that there is no need to add those other private name features because some of the use cases are already served."

If it would be hard for us to make either one of those statements, this is an argument in favor of proceeding with caution on private static, which might lead us to maintain the current split and advance static public fields alone.

Due to the partially overlapping features, the recently raised "hazard", and the lack of experience with these features with these particular semantics in transpilers, it may be worth it to study them longer before proposing for Stage 3, in a way analogous to the approach the pipeline operator proposal is taking.

How should we decide from here?

The contents above are based on conversations between a few TC39 members. I'm interested in expanding the discussion. What do you think of all of this, and how TC39 should proceed?

Note, let's leave out of this thread discussion about the existence of instance private fields and methods, or the # syntax, in this particular thread. There are several other threads in the class fields and private methods repositories which discuss these topics. Unfortunately, I may need to moderate this thread to keep it on topic.

@zenparsing
Copy link
Member

I have found it helpful to view the subclassing hazard with respect to private static fields as an instance of a more general observation:

Since private state access does not traverse the prototype chain, private state is not appropriate for objects that are intended to be used as prototypes.

Consequently, we should not add syntactic support for private state to forms that produce objects that are frequently used as prototypes. Those forms include:

  • The "static" side of class definitions
  • Object literals

On the other hand, the lack of static private fields will be difficult to explain if we have static public fields, instance public fields, and instance private fields. In this case the "hole in the grid" is far more conspicuous than in the case of, for example, "async getters" (which don't make a whole lot of intuitive sense to begin with).

It appears that we're stuck in a classic dilemma - we should take care to make sure that it is not a false dilemma.

@littledan
Copy link
Member Author

I'm surprised to see object literals included here. Object literals can be used as prototypes, but it seems like a stretch to say they are frequently used to the point where private fields in objects become inappropriate altogether.

@zenparsing
Copy link
Member

I'm aware that the case for including object literals in that list is weaker and potentially controversial, and I don't want to sidetrack the discussion over it.

To briefly elaborate though, suppose we allowed the following syntax:

const obj = { #x: 1, get x() { return this.#x } };

Are we giving users enough context to tell when such private field usage is appropriate versus inappropriate? It looks just like a regular object with a cool new feature. Given the fact that we can already use closed-over variables to implement per-object-literal hidden state, it may be better not to tempt users into making a wrong choice.

In the case of class instances, the class syntax (along with the behavior of the builtins) provides the necessary context.

@waldemarhorwat
Copy link

My concerns are:

  • The orthogonality holes this would generate that impose a penalty on everyone learning the language.
  • The unnecessary refactoring difficulties this will impose on users changing between private and public fields for even simple non-inheritance classes.
  • The metaissue of incrementally dividing proposals into subtle small pieces and dribbling them through without agreeing on the big picture of how they will eventually fit in with each other when it's clear we have a diversity of opinions about the issues here.

@syg
Copy link

syg commented Apr 12, 2018

I've come to the position that the "hazard" is not surprising if one internalizes the mental models of both this being a dynamic receiver and private names as lexically scoped property names that are usable on objects of certain provenance. That is, private names combine both lexical scoping and property access, with a crucial restriction on the receiver. Private names seem useful to me, especially with use cases listed in @littledan's post.

So indeed, private names don't participate in prototype inheritance. I'd like to understand better the intuition that private names should participate in prototype inheritance come from, that seems to be against their point as, well, private.

@rauschma
Copy link

I’m in the same camp as Kent: I find public static fields very useful, but would postpone private static fields (since module-private data provides an easy work-around).

@littledan
Copy link
Member Author

littledan commented Apr 17, 2018

@rauschma Are you talking about @zenparsing (Kevin), or who do you agree with?

@rauschma
Copy link

@littledan I’m referring to the tweet by Kent C. Dodds that you quoted in your slides: https://twitter.com/kentcdodds/status/942264424265482240

@littledan
Copy link
Member Author

@rauschma Oh I see. What do you think of "continuity" as a goal here (which I think is similar to what @waldemarhorwat is getting at)?

@rauschma
Copy link

@littledan I’m not sure.

@littledan
Copy link
Member Author

@syg and I discussed this further, and we'd like to put static private fields and methods back in this proposal. As Shu explained, the semantics of private static fields and methods is analogous to the public forms. We'll work on updating this repository for this change and improving educational materials.

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

5 participants