-
Notifications
You must be signed in to change notification settings - Fork 113
Inherited Static properties #43
Comments
It is indeed an error, for exactly the reason you give. |
Is this something that should be considered for a feature? It seems like a footgun that people could easily not think about, and it's a potentially unexpected difference between instance privates and static privates. |
Mmmaybe? @littledan I don't think of it as a difference, though. Private fields are not inherited through prototypes. That's true for static fields just as for instance fields. |
Yeah I'm on the fence. I could kind of see it as expected since the constructor extends the parent, the same way the instance does, it's just that class instances have an established chained initialization process, so it's easy to support in there, where class constructors don't have that. On the other hand, I bet it'd be a pain to make this work and or support in Babel. I don't feel super strongly either way, but I figured it was worth asking. |
I had a prototype crawler originally, and it worked fine. I do see it as a difference between instance and static privates that non-spec-writers will definitely trip over. class Base {
#instance;
static #static;
get() {
return this.#instance;
}
static get() {
return this.#static;
}
}
class Sub extends Base {};
// This works
new Sub().get();
// This throws
Sub.get(); |
Well, I think this is the sort of question that led static private fields to be left out of the earlier private fields proposal by @zenparsing. @erights has brought up the question about whether subclasses should have a separate private field for subclass static fields. If the answer is "no" to that question (as we settled on), I don't see why users would use Do you think we should have separate static private fields for each subclass? Or, do you think I agree with @bakkot that, in a sort of spec-formal way, the current semantics are consistent. But if this would be significantly confusing/underpowered for users, maybe it should be reconsidered. |
I was trying to think of some example that would use Maybe just early error on |
It would be nice if there was a keyword that could be used to s;ways reference the current class, like |
It's hard for me to see how we can generalize that, in a few ways:
@ljharb Why use that keyword when you can use the name of the class? Such a name exists for class expressions which want it, too. |
@littledan to reduce the number of places I have to make a change when I want to rename the class. |
@ljharb That doesn't seem like a primary use case. I imagine it will be more work changing the scattered usage sites than what's inside the class definition. Also, you can use class expressions for an internally visible name (though this will leak through the class's .name...). |
Yes, I understand the use case isn't highly compelling; but it's incredibly common in the airbnb codebase to call static methods from instance methods, or statics from other statics, and it's very messy having to repeat the class name identifier. |
I imagine that you do end up repeating the class name identifier when calling static methods from instance methods... I've heard the opposite argument, that Maybe we'd benefit from searching through code to see how many static methods use |
I think the combination of "static methods using |
Let's break this down:
Overall, I see this thread as a reason to walk back on static private fields, but I don't see a clear way forward. I don't think going up the prototype chain makes sense with the rest of the private field design. Maybe we really should be adding "class instance" private static fields. |
How often do people even call static methods on subclasses? Outside of ES6 classes, do people even bother setting up the inheritance chain? |
Probably not; that's one of the benefits of ES6 classes - that you don't have to remember all the steps. The case that's already working is not without issue - having to hardcore the class name is the issue. I very much doubt people using |
This "repeating the class name is bad practice" idea is new to me. Could you point me to a style guide where it's mentioned (in any language)? |
Here's one for Ruby: https://github.com/bbatsov/ruby-style-guide#def-self-class-methods |
@ljharb In the linked case, a technique is described for not repeating the class name when defining static methods. In JS syntax, the class name is never repeated in that case. Do you have any other reference that talks about not repeating the class name for another case? |
@littledan the overarching principle is DRY - repeating yourself is a bad practice. http://www.korenlc.com/ruby-classes/ mentions this (in the same context; of defining static methods). In PHP 5.5+, this principle is satisfied by |
OK, that's another reference which is recommending the exact same thing--not repeating the class name when defining another static method. It sounds like there's a reasonable class for adding this |
Oh I totally agree it's not worth complicating this proposal; I brought it up because the future possibility of a syntactic |
|
Sorry I'm not being clear :-) I mean, if we decide to disallow static private fields for the time being, then we could later allow them via |
Regarding the "freeze" behavior issue, if we go for "declared public fields are accessors, whether instance or static", then public fields remain mutable under freeze in ways that are consistent with both: Defensive classes could then freeze classes, prototypes, and instances without changing the meaning of their declared properties. A client of an instance could no longer change its behavior by adversarially freezing it, since it would already be frozen. Note that I changed the phrasing from @zenparsing 's to say "encapsulated per-instance state" rather than private field. They are observationally equivalent, so the terminology should not suggest more specificity than needed. It is this rephrasing that enables us to see better how both public static fields choices are compatible with this way of doing public instance fields. |
@erights Thanks for this thoughtful explanation of alternatives and their implications. I take it that, in addition to these possibilities, you are also OK with the current proposal's treatment of public fields; is that right? |
Reluctantly ok, yes. My first choice is still to simply omit them. Always err towards the side of the smaller language. |
(Regarding public-fields-as-accessors)
I don't find the transition cost argument particularly convincing when talking about language features under development. If we are in a position where transpilers and closely-related (but far less important) languages are significantly impinging upon feature development, then the process is broken.
My hope has always been that the implementation of private state/internal slots/etc. would encourage simpler fixed-shape object implementations in the long run.
As with any cooperative and long-lasting endeavor we need to carefully balance path-dependence with our desired long-term future state.
That was my first choice as well; public fields are pretty hard to justify over just putting the assignments directly into the constructor. By using syntactic field definitions, the programmer is trying to express a notion about the fixed shape of instances, and for classes, fixed shape should (IMO) be represented by one feature: private state. An implementation of public fields as private state with accessors also leaves open the possibility of expressing, quite easily, something that we've talked about several times in the past: The primary downside of using the accessor approach (as far as I can tell) is that it might be confusing for programmers that expect public fields to be enumerable own properties. If a programmer wants to use class syntax as a factory for creating object-as-dictionary style objects, then they would still need to use assignments within the constructor. On the other hand, I've always been quite happy imagining a future where classes are primarily Cheers! |
I don't actually see how these differ in how fixed the "shape" is. For private fields, committee members expected private fields to throw exceptions when they're not yet written to, and then be readable from later initializers. Somehow, we need to record that state transition, either in the hidden class of the object or in some other place off to the side. Either way, there are observable state transitions during initialization, so implementation-wise you don't get the biggest benefit of a static shape. Instead, the feature encourages fixed shape by making it so that, when things go well, you always get all the fields by the time the constructor returns. I don't really see how we can do any better, or how accessors would change anything. |
This is decidedly not what I am ever trying to express with public fields, fwiw. I don't care about the fixed shape of instances as a developer; that's something that implementors care about. If I cared, I'd be using a type system. |
We've been continuing the discussion in https://github.com/tc39/proposal-static-class-features/ , while this proposal omits static fields. |
What is the output of:
I want it to equal
'hello'
, but I can't find where the behavior is defined. It's likely just because I'm not familiar enough with the spec text.Maybe it's an error, though? Since I don't think
Sub
has#field
in its[[PrivateFieldValues]]
list, unless there's some super class iteration I'm not seeing.The text was updated successfully, but these errors were encountered: