Summary: prototypes as classes
brendan at mozilla.com
Sat Jul 2 14:04:18 PDT 2011
Remember, I'm the one who is a little sad about classes making the ES.next cutoff. Also I agree with dherman that they have too many option issues and would benefit from reductions ("minimal classes", see ).
On Jul 2, 2011, at 11:46 AM, Angus Croll wrote:
> By unchained prototypes I mean those that directly extend Object.prototype (note that that every built-in prototype specified by ES5 is unchained). Unchained prototypes are gorgeous - one instance defining the properties of many. And it's really not that hard...once you understand the difference between Constructor.prototype and instance.__proto__ (or its symbolic successor) you're all set. The concept itself is very simple - a dynamic archetype to be shared by all my instances: my prototype changes, my instances know about it. I would not want to hide such a smart, simple concept behind the more complex and (in this scenario) less meaningful concept of class.
The difference is just induction from 1 to 2 (assuming you don't extend Object.prototype, which ES5 allows you to do now without breaking for-in loops -- from 2 to 3 if you do extend Object.prototype).
Is a difference in degree really a difference in kind or meaning? I don't think so.
Some empirical results:
5.3 Prototype Chains
One higher-level metric is the length of an object’s prototype chain,
which is the number of prototype objects that may potentially be
traversed in order to find an object’s inherited property. This is
roughly comparable to metrics of the depth of class hierarchies in
class-based languages, such as the Depth of Inheritance (DIT) metric
discussed in . Studies of C++ programs mention a maximum
DIT of 8 and a median of 1, whereas Smalltalk has a median of 3
and maximum of 10. Figure 6 shows that in all but four sites, the
median prototype chain length is 1. Note that we start our graph at
chain length 1, the minimum. All objects except Object.prototype
have at least one prototype, which if unspecified, defaults to the
Object.prototype. The maximum observed prototype chain length
is 10. The majority of sites do not seem to use prototypes for code
reuse, but this is possibly explained by the existence of other ways
directly into a field of an object). The programs that do utilize
prototypes have similar inheritance properties to Java .
Gregor Richards Sylvain Lebresne Brian Burg Jan Vitek
S3 Lab, Department of Computer Science, Purdue University, West Lafayette, IN
Unit-length prototype chain, including Object.prototype -- meaning object initialisers and other new Object uses, not (new C) where C is a user-defined function -- cover a lot of ground.
But the truth is messier. Some JS libraries (and possibly compilers) use deeper prototype chains.
Calling these a mistake across the board is a bit much.
I'm as big a critic of classical OOP as anyone. No argument there, as a generalization.
But JS has C.prototype for user-defined function C, and that makes a two-level prototype chain. Some cohort then go beyond that to make three- or deeper chains. CoffeeScript makes this all pleasant. Why shouldn't ES.next?
No argument from me. Bottom-up, inductive, or constructive learning and system-building are usually best.
We shouldn't make a religious war here. JS has "classes" in its spec for all the built-ins, disclosed by Object.prototype.toString().slice(8,-1). These are represented in-language by constructor functions. Users define constructor functions too.
Since you are not out to prevent users from defining such "classes", why is syntax for the clichéd common version of prototypal inheritance beyond the pale?
> - Mixins allow objects to borrow from an unlimited number of other objects regardless of lineage, while still allowing for organization of functions by type. Mixins are way more flexible than inheritance chains - we can buy behavior types on demand with zero impact on the larger model. Prototypes facilitate Mixins, since a mixin is normally applied to an unchained prototype, another reason why new developers should continue to understand prototypes - 'class' is not a helpful concept where Mixins are concerned.
Mixins require copying properties, and there's nothing lightweight (in usable syntax or runtime cost) in that.
This is logically unsound. Java (let's skip C++, it has functional programming and even closures in the latest and greatest) lacks first class functions (historically; haven't paid attention to the latest), so it relies on classes.
This does not prove that functional languages don't need to support classes as well. Many do support classes, going back to Common Lisp and other Lisps where macros and syntax extensions make it straightforward to provide the kind of sugar proposed for Harmony's classes.
> - The module pattern allows a chunk of data and functionality to be parceled into an object which can be used anywhere without compromising the integrity of the module's code. This ability to package up mini-eco-systems frees the developer from constraints of context that would occur if the same logic were defined as part of a class.
This an independent point. ES.next adds modules for such use-cases. That does not mean prototypal inheritance sugar via classes has no benefit.
> c) How does the class concept square (no pun) with objects like Math, which is not a constructor and \therefore will not, I assume be considered a class? Is Math.pow a const now? If so how will that work when Math is not a class? If not that seems like an inconsistency.
Math is not now and never has been a class in the "represented by a constructor function, with a .prototype" sense. Yes, it has a [[Class]] internal property discoverable via Object.prototype.toString, "Math". This is something we're working to make meta-programmable independently of other interpretations of [[Class]] anyway, for DOM self-hosting. See http://www.wirfs-brock.com/allen/things/es5-technial-notes-and-resources
> Unchained prototypes: Invaluable concept; would be obfuscated by class syntax.
The concept of "chained prototypes" has been in JS from day one. Class syntax adds nothing new semantically.
Sure, we are avoiding the kitchen sink. Again, I'm not in favor of classes with so many open issues -- for them to make progress, we need to simplify and also sanity-check against how prototypal inheritance is used (e.g., with respect to class-side inheritance).
But this does not mean Object.create is enough, any more than the module pattern, a leaky abstraction based on closures, obviates the need for a module system.
I think you're mixing up categories. It's not up to TC39 to change developers' brains by leaving out conveniences for common patterns, if those patterns are not inherently unsafe or otherwise bad.
Even unchained prototype use-cases where there's a constructor function to initialize per-instance state benefit from class syntax.
At this point you've lost me. In no sense is class as prototypal sugar "classical OOP" that differs observably from prototypal inheritance in JS. Even class-side (constructor) inheritance would be done via <| or __proto__. There's nothing new and different here. No vtbls, no interfaces as in Java, no non-property "fields" or "final methods".
I think you're mixing up good skepticism of OOP-is-all-you-need with classes-as-prototypal-sugar FUD. Would changing "class", the reserved word (we can't really unreserve and pick another), to "constructor", help? If not, why not?
Allen's efforts with <| and generalized 'super' also reinforce the point that classes are prototypal inheritance sugar, and only that. Jeremy's work with CoffeeScript demonstrates the same point. There is no magic runtime implementing classical OOP differently, other than class-side (constructor) inheritance via a little property-copying helper code, for want of <| or universal writable __proto__.
I think you are protesting too much.
> On Wed, Jun 29, 2011 at 3:08 PM, Brendan Eich <brendan at mozilla.com> wrote:
> On Jun 29, 2011, at 2:24 PM, Axel Rauschmayer wrote:
> >> That's all neat in a kind of desugaring ftw, nerdcore way, but tl;dr -- users don't care and they want to write new C and have it just work (mostly; granted some crack the code, for a few good and bad purposes).
> > Note that I am arguing from the perspective of a language that only has PaCs, versus a language that only has constructor functions. If you don’t, then we don’t disagree.
> That was not clear, because we keep coming back to JS as it is. That's where I want to land, so arguing about what-if's and might-have-been's is not my cup of tea.
> > You argue that constructor functions are more intuitive at the user level (to *all* people) and that PaCs wouldn’t “just work”
> I never wrote anything like either of those things, certainly not "to *all* people". Indeed I was the one pointing out that your universals did not apply to all people.
> Last concrete disagreement we had was over new C() vs. C() in the current language being notably different from new C() vs. C.constructor() in the alternate-reality language with prototypes as classes.
> es-discuss mailing list
> es-discuss at mozilla.org
-------------- next part --------------
An HTML attachment was scrubbed...
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Figure 6.png
Size: 58038 bytes
Desc: not available
More information about the es-discuss