prototype focus

# Peter Michaux (14 years ago)

There seems to have been a lot of fuss here about focusing on prototypes first rather than on constructor functions. As it stands now, I don't see how JavaScript makes focusing on prototypes difficult.

// focus on the prototype first // Make it non-abstract. // Call it "zero" not "number". // (Another example: Call the object "adam" not "person".)

var zero = { realPart: 0, imagPart: 0, getMagnitude: function() { return Math.sqrt(this.realPart * this.realPart + this.imagPart * this.imagPart); } };

// JavaScript makes it possible to have a variety of constructors // for objects that have zero as their prototype. // Yes the constructor property is gone. Is that actually a problem?

function Real(r) { this.realPart = r; } Real.prototype = zero;

function Imaginary(i) { this.imagPart = i; } Real.prototype = zero;

function Complex(r, i) { this.realPart = r; this.imagPart = i; } Complex.prototype = zero;

// Now make some objects.

var two = new Real(2); var i = new Imaginary(2); var oneone = new Complex(1, 1);

Isn't that prototype-focused enough?

Peter

# Mark S. Miller (14 years ago)

On Thu, Jun 30, 2011 at 12:54 PM, Peter Michaux <petermichaux at gmail.com>wrote:

There seems to have been a lot of fuss here about focusing on prototypes first rather than on constructor functions. As it stands now, I don't see how JavaScript makes focusing on prototypes difficult.

// focus on the prototype first // Make it non-abstract. // Call it "zero" not "number". // (Another example: Call the object "adam" not "person".)

var zero = { realPart: 0, imagPart: 0, getMagnitude: function() { return Math.sqrt(this.realPart * this.realPart + this.imagPart * this.imagPart); } };

// JavaScript makes it possible to have a variety of constructors // for objects that have zero as their prototype. // Yes the constructor property is gone. Is that actually a problem?

function Real(r) { this.realPart = r; } Real.prototype = zero;

function Imaginary(i) { this.imagPart = i; } Real.prototype = zero;

Nit: Should be "Imaginary.prototype = zero;"

# Axel Rauschmayer (14 years ago)

What problem have you solved by giving the prototype a name? The proposal is not (just) about giving prototypes names.

Problems that both prototypes-as-classes (PAC) and class literals (CL) are trying to solve are:

  • Subclassing is hard and not directly supported by the language: connecting prototypes, chaining constructors, super-references.
  • Class properties are not inherited (CoffeeScript copies them “manually”).

If none of these issues are involved then today’s constructor functions are OK.

Pros of each approach: (1) Pro CL: no need to support two kinds of classes: prototypes and constructor functions. (2) Pro PAC: look the same as class literals, but don’t need an extra translation step.

(1) weighs heavily and will probably make it impossible to sell PAC to TC39. There is the precedent of Python having two styles of classes [1] at the same time, but I don’t think they diverged as heavily as constructor functions and PAC.

If you want to argue against PAC benefits, take a look at Sect. 3 here: www.2ality.com/2011/06/prototypes-as-classes.html#3

[1] stackoverflow.com/questions/54867/old-style-and-new-style-classes-in-python

Axel

# Peter Michaux (14 years ago)

On Thu, Jun 30, 2011 at 4:46 PM, Axel Rauschmayer <axel at rauschma.de> wrote:

What problem have you solved by giving the prototype a name?

The names I choose makes it clear to the reader that the prototype is one element (zero) in a set of elements (complex numbers).

A name gives a reference to the object that is not related to any constructor function. Constructor functions are something separate from the prototype object (zero).

The proposal is not (just) about giving prototypes names.

Understood but it seemed that the proposal started from the idea that JavaScript needs to get back to its prototype roots. I don't think JavaScript has ever been far from its prototype roots especially if the programmer shifts to thinking about a prototype object instead of thinking about a functions prototype property.

Problems that both prototypes-as-classes (PAC) and class literals (CL) are trying to solve are:

  • Subclassing is hard and not directly supported by the language: connecting prototypes, chaining constructors, super-references.

Object.getPrototypeOf(this).foo.call(this) is pretty long.

  • Class properties are not inherited

Class property inheritance is definitely a secondary issue compared with the instance objects themselves.

(CoffeeScript copies them “manually”).

Which I think is unsatisfactory and has a lot of gotchas when some properties are arrays and other properties have primitive values.

--

It seems to me that perhaps the PaC drifted too far or perhaps started too far from what JavaScript has already. If the idea is to shift the focus more towards prototypes, then starting from something like what I've written and adding "super" syntax would be more consistent with what JavaScript already has.

Peter

# Axel Rauschmayer (14 years ago)

I don't think JavaScript has ever been far from its prototype roots especially if the programmer shifts to thinking about a prototype object instead of thinking about a functions prototype property.

That is basically the point that the proposal tries to make. Have you taken a look at the library code? It is very short and not a radical overhaul. dl.2ality.com/dl/2011/06/Proto.js

Note how below, there is always an extra "prototype" in there with constructor functions.

Super-calls (there will be syntactic sugar for this):

  • Constructor functions: Superclass.prototype.foo.call(this)
  • PAC: Superclass.foo.call(this)

Subclassing:

  • Constructor functions: Subclass.prototype = Object.create(Superclass.prototype)
  • PAC: let Subclass = Object.create(Superclass)

Instanceof (internally):

  • Constructor functions: o instanceof C === C.prototype.isPrototypeOf(o)
  • PAC: o instanceof C === C.isPrototypeOf(o)

Problems that both prototypes-as-classes (PAC) and class literals (CL) are trying to solve are:

  • Subclassing is hard and not directly supported by the language: connecting prototypes, chaining constructors, super-references.

Object.getPrototypeOf(this).foo.call(this) is pretty long.

See above.

It seems to me that perhaps the PaC drifted too far or perhaps started too far from what JavaScript has already. If the idea is to shift the focus more towards prototypes, then starting from something like what I've written and adding "super" syntax would be more consistent with what JavaScript already has.

“too far from what JavaScript has already” is very vague. How so? With class literals, your code will look like PAC, anyway. Have you taken a look at Sect. 3? Do you agree that the mentioned conceptual simplifications hold? www.2ality.com/2011/06/prototypes-as-classes.html#3

I find Brendan’s anti-PAC argument much more convincing: that all people might find it more natural to think in terms of constructors than in terms of prototypes. If that is the case then PAC would be a bad idea. The other convincing anti-PAC argument is that it is a bad idea to have two class mechanisms.

# Tim Smart (14 years ago)

I quite the current prototype model we have in ecma5. My only gripes would be that prototype to too wordy, and __proto__ needs to become standard. If you replaced prototype with :: or something everything would be swell.

function Parent (name) { this.name = name || this.constructor.DEFAULT_NAME } Parent.DEFAULT_NAME = 'bob'

function Child (name, age) { Parent.call(this, name) this.age = age } Child.proto = Parent Child::proto = Parent.prototype Child::describe = function () { return 'I am called ' + this.name + ' and I am ' + this.age + ' years old' }

Tim.

# Axel Rauschmayer (14 years ago)

What I’m asking for is the following (and I might not convince anyone, but just consider the possibility):

If you are using a constructor function C only once, during construction and otherwise always use C.prototype, wouldn’t it be better to focus on C' = C.prototype Then you would use C'.constructor (or C'.initialize or C'.init) once and C' the remaining times.

To paraphrase: Hasn’t the prototype been always the core construct? And if yes, shouldn’t we make that explicit?

I would like people to agree with me that there is some merit in that. After that, there are still arguments against PAC, though...

As for proto: Have you taken a look at the <| operator? It does everything you want, it’s really cool.

# Axel Rauschmayer (14 years ago)

BTW: I like the :: proposal (Brendan will remind you that it’s already taken, though...). What you are in fact creating is the illusion that the class name refers to the prototype (with PAC, the :: would turn into a dot).

# Irakli Gozalishvili (14 years ago)

I absolutely agree with Alex and have few other points:

  1. Does this code looks familiar ?

function Foo(options) { .... } Foo.prototype.do_foo_job = function() { ... }

function Bar(options) { if (!(this instanceof Bar)) return this new Bar(options); Foo.apply(this, arguments); } Bar.prototype.do_bar_job = function() { .... }

With focus on prototype this is so much simpler:

var Foo = Object.extend({ initialize: function(options) { ... }, do_foo_job: function() { ... } })

# Irakli Gozalishvili (14 years ago)

Sorry I did not intended to send email yet:

So here is my points:

  1. Does this looks familiar (version with syntax highlighting gist.github.com/1058534)

function Foo(options) { .... } Foo.prototype.do_foo_job = function() { ... }

function Bar(options) { if (!(this instanceof Bar)) return this new Bar(options); Foo.apply(this, arguments); } Bar.prototype = Object.create(Foo.prototype); Bar.prototype.do_bar_job = function() { .... };

With focus on prototypes it's as simple as this: var Foo = Object.extend({ initialize: function(options) { ... }, do_foo_job: function() { ... } })

var Bar = Foo.extend({ do_bar_job: function() { ... } })

Why should not be I able to reuse initialization function, or why do I have to create constructor function for all classes / subclasses ?

  1. With a constructor focused classes you end up maintaing two sets of properties: 1. prototype properties and 2. (java static like) constructor properties, which adds complexity requires dedicated syntax in declarative classes syntax.

  2. It could be my personal, internal conflict, but I seriously can't see any reason for constructor to be a special among all other methods, by having privilege to hold a reference to a prototype object (I don't even mention that this reference is circular by default). Overall prototype property feels like hack to emulate Java like classes.

  3. With focus on prototypes there is potential to further reduce language syntax (hopefully new, instanceof will die off) instead of cluttering even further.

-- Irakli Gozalishvili Web: www.jeditoolkit.com Address: 29 Rue Saint-Georges, 75009 Paris, France (goo.gl/maps/3CHu)

# Sean Eagan (14 years ago)

On Fri, Jul 1, 2011 at 8:45 AM, Irakli Gozalishvili <rfobic at gmail.com> wrote:

why do I have to create constructor function for all classes / subclasses ?

This could be handled by class literals by allowing for default constructors. If one doesn't provide a constructor, the following one could be provided:

constructor(... args) { super(... args); }

# Mark S. Miller (14 years ago)

On Fri, Jul 1, 2011 at 7:18 AM, Sean Eagan <seaneagan1 at gmail.com> wrote:

On Fri, Jul 1, 2011 at 8:45 AM, Irakli Gozalishvili <rfobic at gmail.com> wrote:

why do I have to create constructor function for all classes / subclasses ?

This magic trades confusion for convenience. In any earlier version of the proposal, I actually had a traditional default constructor, the equivalent of

constructor() { super(); }

but others argued against it. Sorry don't remember who. There were plenty of off list discussions to gather consensus. On this one, I didn't feel strongly either way, but I eventually agreed with the objectors. When in doubt, throw it out -- even more important in language design than in writing.

This could be handled by class literals by allowing for default constructors. If one doesn't provide a constructor, the following one could be provided:

constructor(... args) { super(... args); }

This one I object to because those used to other languages that provide such default constructors will only call the super constructor with no arguments. Though now that you mention it, I can see why JSers without exposure to other languages may naturally expect your's.

With two expectations for the semantics of something that does not appear in the code, and without a static or dynamic rejection to prevent progress of the code written to the wrong assumption, I now finally feel strongly about this. The critics were right -- we should not provide any default constructor. Thanks for pointing out the problem case.

# Irakli Gozalishvili (14 years ago)

On Friday, 2011-07-01 at 16:38 , Mark S. Miller wrote:

On Fri, Jul 1, 2011 at 7:18 AM, Sean Eagan <seaneagan1 at gmail.com (mailto:seaneagan1 at gmail.com)> wrote:

On Fri, Jul 1, 2011 at 8:45 AM, Irakli Gozalishvili <rfobic at gmail.com (mailto:rfobic at gmail.com)> wrote:

why do I have to create constructor function for all classes / subclasses ?

This magic trades confusion for convenience. In any earlier version of the proposal, I actually had a traditional default constructor, the equivalent of

constructor() { super(); }

but others argued against it. Sorry don't remember who. There were plenty of off list discussions to gather consensus. On this one, I didn't feel strongly either way, but I eventually agreed with the objectors. When in doubt, throw it out -- even more important in language design than in writing.

This could be handled by class literals by allowing for default constructors. If one doesn't provide a constructor, the following one could be provided:

constructor(... args) { super(... args); }

This one I object to because those used to other languages that provide such default constructors will only call the super constructor with no arguments. Though now that you mention it, I can see why JSers without exposure to other languages may naturally expect your's.

With two expectations for the semantics of something that does not appear in the code, and without a static or dynamic rejection to prevent progress of the code written to the wrong assumption, I now finally feel strongly about this. The critics were right -- we should not provide any default constructor. Thanks for pointing out the problem case.

Do you think prototype focused version:

Prototype.new(foo);

also suggest wrong assumptions ? If not (which I think is the case) is another + in favor of prototype focus.

# Sean Eagan (14 years ago)

On Fri, Jul 1, 2011 at 9:38 AM, Mark S. Miller <erights at google.com> wrote:

This could be handled by class literals by allowing for default constructors.  If one doesn't provide a constructor, the following one could be provided:

constructor(... args) {  super(... args); }

This one I object to because those used to other languages that provide such default constructors will only call the super constructor with no arguments.

I'm sure there is good reason to only default the zero-argument constructor in languages with multiple constructors per class, however, ES makes it obvious that there is only one constructor per class, which class literal syntax reinforces further, so I don't think users would have a hard time understanding that "a default constructor merely forwards its arguments to the superclass constructor". I don't think users would ever expect arguments passed to a default constructor not to be forwarded to the superclass constructor.

# Quildreen Motta (14 years ago)

Ah, oh yes. I think I fail at mailing lists :3

2011/7/1 Peter Michaux <petermichaux at gmail.com>

# Brendan Eich (14 years ago)

On Jul 1, 2011, at 2:21 AM, Tim Smart wrote:

I quite the current prototype model we have in ecma5. My only gripes would be that prototype to too wordy,

Do you use it that often?

and __proto__ needs to become standard.

The <| operator is the future there. proto won't be standardized in any form, certainly not writable proto.

If you replaced prototype with :: or something everything would be swell.

Yeah, CoffeeScript does that. However, strawman:guards wants :: for optional declaration guards. Could these be disjoint from :: in expressions, used as you want? Not sure grammatically, might be doable without ambiguity -- but it seems a bad idea to have :: mean two very different things.

Again I'm wondering why "C.prototype." is written so much. You can decorate constructor prototypes that way, for sure, but you can also set C.prototype = {...} once and avoid saying .prototype. a lot.

# Brendan Eich (14 years ago)

On Jul 1, 2011, at 6:19 AM, Irakli Gozalishvili wrote:

With focus on prototype this is so much simpler:

var Foo = Object.extend({ initialize: function(options) { ... }, do_foo_job: function() { ... } })

With <| and 'super' in functions, I think you are set. It's hard to add more. The main debate is about whether this is enough, or do classes as sugar provide enough added value?

(But you didn't show Bar as well as Foo.)

# Mike Shaver (14 years ago)

On Jul 1, 2011 1:14 PM, "Brendan Eich" <brendan at mozilla.com> wrote:

On Jul 1, 2011, at 2:21 AM, Tim Smart wrote:

I quite the current prototype model we have in ecma5. My only gripes

would be that prototype to too wordy,

Do you use it that often?

15 years ago, writing an overwrought prototype hierarchy for a server-side JS project, I experimented with

P = "prototype";

function Thing () { } Thing[P] = { ... }

Wasn't worth it, I decided, because it didn't match the (then scant, now abundant) documentation of delegation. Same principle applies here for adding synonyms, I think.

Mike

# Irakli Gozalishvili (14 years ago)

On Friday, 2011-07-01 at 19:31 , Brendan Eich wrote:

On Jul 1, 2011, at 6:19 AM, Irakli Gozalishvili wrote:

With focus on prototype this is so much simpler:

var Foo = Object.extend({ initialize: function(options) { ... }, do_foo_job: function() { ... } })

With <| and 'super' in functions, I think you are set. It's hard to add more. The main debate is about whether this is enough, or do classes as sugar provide enough added value?

(But you didn't show Bar as well as Foo.)

Sorry I don't understand what did I failed to show in Bar ?

My intent was to show that classes don't necessary need to have own constructors / initialization methods as they can be simply inherited via prototype chain. Or did you meant something else ?

# Axel Rauschmayer (14 years ago)

With <| and 'super' in functions, I think you are set. It's hard to add more. The main debate is about whether this is enough, or do classes as sugar provide enough added value?

<|, super and possibly some support for subclassing (to set up the constructor property in subclasses etc.) would cover a lot of ground (if we can’t have something more sophisticated – I’m wondering if we are at a “now or never” point in this regard. That is: If we don’t make breaking changes for <insert your favorite class-like construct> now, will we ever?).

One thing that should be kept in mind is tool support: How do IDEs get enough information for auto-expansion etc. Lack of a good IDE still makes it slightly painful to write JavaScript for me (while I really like the language). Is there someone with experience in that department that could comment? I’m guessing that type guards would help(?) What else?

IDE support would be a pro-class literal argument, I guess. Another question: If everything is encapsulated (instantiation, subclassing, instanceof, etc.) then it doesn’t matter any more what goes on under the hood.

# Brendan Eich (14 years ago)

On Jul 1, 2011, at 8:40 AM, Irakli Gozalishvili wrote:

On Friday, 2011-07-01 at 16:38 , Mark S. Miller wrote:

With two expectations for the semantics of something that does not appear in the code, and without a static or dynamic rejection to prevent progress of the code written to the wrong assumption, I now finally feel strongly about this. The critics were right -- we should not provide any default constructor. Thanks for pointing out the problem case.

Do you think prototype focused version:

Prototype.new(foo);

also suggest wrong assumptions ?

What's the constructor protocol here? If I don't implement Prototype.new or Prototype.constructor, then what in the super-prototype is called, with what arguments?

If not (which I think is the case) is another + in favor of prototype focus.

This scoring is silly. The trouble with OOP default-constructor protocols is there are so many of them. Having none, as Mark points out, avoids trouble. Having a different one from any in the language as conventionally used (including built-ins, DOM, etc.), even with prototype focus, does not guarantee anything.

As Axel has acknowledged, any .new protocol will coexist with constructor.prototype, forever. Adding explicit super-constructor calling is part of the classes deal, whatever the focus (constructor or prototype). Defaulting the super-constructor call or even the constructor implementation is trouble.

# Brendan Eich (14 years ago)

On Jul 1, 2011, at 2:28 PM, Irakli Gozalishvili wrote:

On Friday, 2011-07-01 at 19:31 , Brendan Eich wrote:

On Jul 1, 2011, at 6:19 AM, Irakli Gozalishvili wrote:

With focus on prototype this is so much simpler:

var Foo = Object.extend({ initialize: function(options) { ... }, do_foo_job: function() { ... } })

With <| and 'super' in functions, I think you are set. It's hard to add more. The main debate is about whether this is enough, or do classes as sugar provide enough added value?

(But you didn't show Bar as well as Foo.)

Sorry I don't understand what did I failed to show in Bar ?

I was replying to your message sent too soon; sorry, I should have looked ahead for your complete followup.

My intent was to show that classes don't necessary need to have own constructors / initialization methods as they can be simply inherited via prototype chain. Or did you meant something else ?

No, I was looking for your version of Bar as prototype not constructor function.

# Irakli Gozalishvili (14 years ago)

-- Irakli Gozalishvili Web: www.jeditoolkit.com Address: 29 Rue Saint-Georges, 75009 Paris, France (goo.gl/maps/3CHu)

On Friday, 2011-07-01 at 23:51 , Brendan Eich wrote:

On Jul 1, 2011, at 8:40 AM, Irakli Gozalishvili wrote:

On Friday, 2011-07-01 at 16:38 , Mark S. Miller wrote:

With two expectations for the semantics of something that does not appear in the code, and without a static or dynamic rejection to prevent progress of the code written to the wrong assumption, I now finally feel strongly about this. The critics were right -- we should not provide any default constructor. Thanks for pointing out the problem case.

Do you think prototype focused version:

Prototype.new(foo);

also suggest wrong assumptions ?

What's the constructor protocol here? If I don't implement Prototype.new or Prototype.constructor, then what in the super-prototype is called, with what arguments?

It was stupid to assume it was obvious :) I think this is a best answer: Gozala/selfish/blob/master/selfish.js

Or short version is

var Prototype = { new: function() { var object = Object.create(this); object.initialize.apply(object, arguments); return object; }, initialize: function() {} }

var Point = Prototype <| { initialize: function (x, y) { this.x = x this.y = y } }

var point = Point.new(11, 17)

There are more examples here: Gozala/selfish

If not (which I think is the case) is another + in favor of prototype focus. This scoring is silly.

Agreed :)

The trouble with OOP default-constructor protocols is there are so many of them. Having none, as Mark points out, avoids trouble. Having a different one from any in the language as conventionally used (including built-ins, DOM, etc.), even with prototype focus, does not guarantee anything.

As Axel has acknowledged, any .new protocol will coexist with constructor.prototype, forever.

I'm more optimistic about, because currently it's a pain and I think any better alternative will win.