-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Editorial: Remove 'designed to be subclassable' #2360
Conversation
Hi, I thought it'd be good to start a discussion around the removal of this phrase (which was mentioned in plenary) in order to provide a basis for the language in Temporal to be consistent with. Please do suggest alternatives 😄 |
spec.html
Outdated
@@ -24492,7 +24492,7 @@ <h1>The Object Constructor</h1> | |||
<li>is the initial value of the *"Object"* property of the global object.</li> | |||
<li>creates a new ordinary object when called as a constructor.</li> | |||
<li>performs a type conversion when called as a function rather than as a constructor.</li> | |||
<li>is designed to be subclassable. It may be used as the value of an `extends` clause of a class definition.</li> | |||
<li>may be used as the value of an `extends` clause of a class definition.</li> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is definitely better! but:
<li>may be used as the value of an `extends` clause of a class definition.</li> | |
<li>may be used as the value of an `extends` clause of a class definition, but is not intended to be subclassed.</li> |
perhaps? (at least for all the non-nullish primitive constructors, where it just doesn't make sense to ever subclass them)
spec.html
Outdated
@@ -27366,7 +27366,7 @@ <h1>The Date Constructor</h1> | |||
<li>creates and initializes a new Date object when called as a constructor.</li> | |||
<li>returns a String representing the current time (UTC) when called as a function rather than as a constructor.</li> | |||
<li>is a function whose behaviour differs based upon the number and types of its arguments.</li> | |||
<li>is designed to be subclassable. It may be used as the value of an `extends` clause of a class definition. Subclass constructors that intend to inherit the specified Date behaviour must include a `super` call to the Date constructor to create and initialize the subclass instance with a [[DateValue]] internal slot.</li> | |||
<li>may be used as the value of an `extends` clause of a class definition. Subclass constructors that intend to inherit the specified Date behaviour must include a `super` call to the Date constructor to create and initialize the subclass instance with a [[DateValue]] internal slot.</li> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Date seems like it works fine as being subclassable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar response here as above for Error.
spec.html
Outdated
@@ -30767,7 +30767,7 @@ <h1>The RegExp Constructor</h1> | |||
<li>is <dfn>%RegExp%</dfn>.</li> | |||
<li>is the initial value of the *"RegExp"* property of the global object.</li> | |||
<li>creates and initializes a new RegExp object when called as a function rather than as a constructor. Thus the function call `RegExp(…)` is equivalent to the object creation expression `new RegExp(…)` with the same arguments.</li> | |||
<li>is designed to be subclassable. It may be used as the value of an `extends` clause of a class definition. Subclass constructors that intend to inherit the specified RegExp behaviour must include a `super` call to the RegExp constructor to create and initialize subclass instances with the necessary internal slots.</li> | |||
<li>may be used as the value of an `extends` clause of a class definition. Subclass constructors that intend to inherit the specified RegExp behaviour must include a `super` call to the RegExp constructor to create and initialize subclass instances with the necessary internal slots.</li> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sadly, RegExp was explicitly designed to be subclassable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still curious. Does anyone use subclassable RegExp in wild? I never seen that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neither have i, but the committee decided not to remove that plumbing, and it was definitely designed for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sadly, RegExp was explicitly designed to be subclassable
Doesn't mean we need to mention that in the specification, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it’s reasonable to denote the things that are built for it, to separate them from the ones that aren’t.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If “properly work” means “RegExp subclasses don’t work, which is fine because nobody actually uses them except for core-js feature detection”, then they’d just use those methods directly. If by “properly work” you mean that RegExp subclasses continue to work, then no, of course those are required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ljharb by "properly" work I mean how to make work extended transpiled functionality of RegExp
- NCG in this case - work with String.prototype.match
and String.prototype.replace
. It's absolutely not related to polyfills - it's the implementation of ES2018 feature on ES2015. You say that "they could drastically simplify their output" - I have no ideas how it could be implemented without ES2015 subclassing with delegation to .exec
and @@replace
how it's implemented at this moment or without patching built-ins (unacceptable on the transpilers side).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For that, you’re probably right - but “making transpilers possible” isn’t a primary concern when designing features.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, it's a significant plus.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When possible, yes, but it's not worth the complexity and performance hits that the RegExp system takes. Unfortunately, we can't remove it because there's no practical way to tease apart polyfill feature detections from real code when gathering usage data.
spec.html
Outdated
@@ -25626,7 +25626,7 @@ <h1>The Error Constructor</h1> | |||
<li>is <dfn>%Error%</dfn>.</li> | |||
<li>is the initial value of the *"Error"* property of the global object.</li> | |||
<li>creates and initializes a new Error object when called as a function rather than as a constructor. Thus the function call `Error(…)` is equivalent to the object creation expression `new Error(…)` with the same arguments.</li> | |||
<li>is designed to be subclassable. It may be used as the value of an `extends` clause of a class definition. Subclass constructors that intend to inherit the specified Error behaviour must include a `super` call to the Error constructor to create and initialize subclass instances with an [[ErrorData]] internal slot.</li> | |||
<li>may be used as the value of an `extends` clause of a class definition. Subclass constructors that intend to inherit the specified Error behaviour must include a `super` call to the Error constructor to create and initialize subclass instances with an [[ErrorData]] internal slot.</li> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I do see and use subclassing on the Error constructor. Isn't it is designed subclassable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error doesn't have any methods outside of toString
, which side steps the issues with subclassing. Still I don't see any reason to deviate from the proposed language here. I see this PR as removing encouragement, and IMO, the fact that Error can be successfully subclassed doesn't mean the spec needs to encourage it over other built-ins that can't be easily successfully subclassed.
Further, by removing the encouragement, we're also saying something about future proposals for extending Error: that they shouldn't accommodate subclassing beyond the constructor being usable in extends
.
spec.html
Outdated
@@ -31492,7 +31492,7 @@ <h1>The Array Constructor</h1> | |||
<li>creates and initializes a new Array exotic object when called as a constructor.</li> | |||
<li>also creates and initializes a new Array object when called as a function rather than as a constructor. Thus the function call `Array(…)` is equivalent to the object creation expression `new Array(…)` with the same arguments.</li> | |||
<li>is a function whose behaviour differs based upon the number and types of its arguments.</li> | |||
<li>is designed to be subclassable. It may be used as the value of an `extends` clause of a class definition. Subclass constructors that intend to inherit the exotic Array behaviour must include a `super` call to the Array constructor to initialize subclass instances that are Array exotic objects. However, most of the `Array.prototype` methods are generic methods that are not dependent upon their *this* value being an Array exotic object.</li> | |||
<li>may be used as the value of an `extends` clause of a class definition. Subclass constructors that intend to inherit the exotic Array behaviour must include a `super` call to the Array constructor to initialize subclass instances that are Array exotic objects. However, most of the `Array.prototype` methods are generic methods that are not dependent upon their *this* value being an Array exotic object.</li> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same, subclassing Array is really an important feature in ES6, why discourage that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disagree? I guess it depends on what you mean by subclassing, since in JS it encompasses stuff like species construction, and that methods can deal with non-Array objects that still have the Array exotic objects' slots and essential methods.
What about subclassing Arrays is important beyond being able to extend
them?
spec.html
Outdated
@@ -34249,7 +34249,7 @@ <h1>The Map Constructor</h1> | |||
<li>is the initial value of the *"Map"* property of the global object.</li> | |||
<li>creates and initializes a new Map object when called as a constructor.</li> | |||
<li>is not intended to be called as a function and will throw an exception when called in that manner.</li> | |||
<li>is designed to be subclassable. It may be used as the value in an `extends` clause of a class definition. Subclass constructors that intend to inherit the specified Map behaviour must include a `super` call to the Map constructor to create and initialize the subclass instance with the internal state necessary to support the `Map.prototype` built-in methods.</li> | |||
<li>may be used as the value in an `extends` clause of a class definition. Subclass constructors that intend to inherit the specified Map behaviour must include a `super` call to the Map constructor to create and initialize the subclass instance with the internal state necessary to support the `Map.prototype` built-in methods.</li> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as Array/Set/Error case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same question as above then: what about the subclassing facilities, aside from being able to extend Maps, is important?
In my personal programming use case, I have subclasses the following classes and found they're really useful. I don't think we should discourage those classes to be extended.
|
Many of these were originally designed to be subclassable. All of them work to at least some extent in an However, we aren't obligated to mention everything design decision we've ever made. My preference is to just drop mention of subclassing from all built-in constructors. We don't need to take a position on whether it's a good idea to subclass these, whether in general or for some particular subset. We can simply not discuss it at all. |
I think it’s very important that we actively discourage subclassing of the things that aren’t meant for it. I’m not sure how to do that without explicitly mentioning that some things are meant for it. |
Personally I agree with @bakkot here: removing the phrase doesn't discourage subclassing. But I'm not an editor, so having given my opinion I'll happily change this to reflect whatever comes out of the discussion, and then change Temporal accordingly. |
I think this goes beyond a purely editorial question, personally, since user expectations from prose in the spec do often impact normative behaviors - just like the same discussion around Temporal. |
Strong agree with @bakkot. "Subclassing" as an umbrella term encompasses several mechanisms, some of which I actively despise, and some of which are fine. I like this PR in that it calls out a particular mechanism as being actively maintained for future evolution of the language, and removing encouragement for using the whole set of mechanisms. @ljharb and @Jack-Works, if you feel some of those mechanisms should be encouraged, then let's discuss those. I'm otherwise willing to die on the hill of removing blanket encouragement of "subclassing". |
do you mean https://github.com/tc39/proposal-rm-builtin-subclassing#taxonomy-of-subclassing? |
@syg I'm not attached to the term "subclassing" for the precise reason you mention - that it conflates multiple mechanisms. I think it is important to explicitly distinguish the builtins that can meaningfully be the RHS of an |
I agree with @bakkot that this is very subjective and it's apparent that people don't share the same definition of "subclass". Some of it is provably untrue ("must include a |
What's being replaced? This PR drops the "designed to be subclassable" language, the other language is existing. |
@syg You’re right, sorry — that stemmed from misreading. The real changes here seem like improvements to me. |
Have the editors had a chance to discuss this? It seems like it is blocked on whether the removal of the phrase "is designed to be subclassable" is interpreted as a deprecation, or as a neutral removal of encouragement. |
Editors discussed this and are happy about it as-is among ourselves. Since there seems to be disagreement about whether this is strictly an editorial decision, I've put together a very short presentation and will ask that we get consensus on this particular change at the next meeting, emphasizing that a.) this PR does not take a position against (or for) subclassing any particular constructors and b.) the current state of affairs, which says that Boolean is "designed to be subclassable", is silly. If people want to call out some particular subset as being "designed to be subclassable", and/or call out some other subset as explicitly not for subclassing, they are welcome to drive that through committee as a followup. |
"designed to be subclassable" was used in the ES6 specification with a very specific meaning:
ES6 Boolean was, in fact, "defined to be subclassable" exactly according to those criteria. Specifically, Boolean.prototype.toString and valueOf are specified such that they make use of [[BooleanData]] internal slot of a Boolean subclass. There may be very little utility in subclassing Boolean, but if somebody does it will work. The incorrect operation of ad hoc subclasses of Array and other builtins was something that developers complained about in the ES3/ES5 era. Fixing that general problem was an ES6 objective. For both internal and external consistency "defined to be subclassable" was the default design principle for ES6 builtins, even with there was minimal utility in subclassing some builtins. Only in a few cases such as Symbol, were there strong reasons to violate the consistency and design principle and make a builtin non-"subclassable". Because "designed to be subclassable" was the ES6 norm, that label arguably was not need on every builtin constructor. But as a change from prior editions of the specification the label served as an important notice to both ES implementors and developers. Perhaps it is no longer needed, but the fact that has been this ongoing discussion make me think it still has value. |
@allenwb, thanks for giving the history. It's the opinion of the current editor group that the sense of "designed to be subclassable" used in your comment is adequately captured by the "may be used as the value of an |
1320e04
to
3d0c24c
Compare
As discussed during the March 2021 plenary, this note on "is designed to be subclassable" can be read as an encouragement or value judgement on whether it is desirable to subclass built-in classes like Boolean. We don't want to actively encourage this.
1c01ea5
to
a44f010
Compare
See tc39/ecma262#2360 Bring the language around this in line with the main ECMA-262 text, using the same unordered list form and the same bullet points everywhere.
See tc39/ecma262#2360 Bring the language around this in line with the main ECMA-262 text, using the same unordered list form and the same bullet points everywhere.
As discussed during the March 2021 plenary, this note on "is designed to be subclassable" can be read as an encouragement or value judgement on whether it is desirable to subclass built-in classes like Boolean. We don't want to actively encourage this.
As discussed during the March 2021 plenary, this note on "is designed to
be subclassable" can be read as an encouragement or value judgement on
whether it is desirable to subclass built-in classes like Boolean. We
don't want to actively encourage this.