The class operator: a bridge between object and function exemplers

# Allen Wirfs-Brock (14 years ago)

(I'm going to write this up as a strawman, this list gets first shot at it...)

Introduction

For ES.next we all want to provide better ways for ES programers to define named abstractions over objects. One way to do this that we have discussed is "Object exemplars" (www.mail-archive.com/[email protected]/msg10365.html )

Object exemplars essentially implement the self language style of object abstraction where prototype objects are given names and used as the named abstraction that represents a family of related objects. Object exemplars typically are instantiated by applying the new operator to a specific named instance of the abstraction. Object exemplars also typically define an initialization method that is called when a new instance is created. For the ES implementation of object exemplars, this initialization method is named "constructor". For example:

let Point = { x:0, y,0, constructor(x,y} { this.x=x; this.y=y; } };

let p = new Point(1,2);

Using object exemplars, the named abstraction is itself an instance of the abstraction. Some people seem to be perfect happy to only have and use this form of object abstraction. However, others prefer to think in a more "classical" style where the named object representing an abstraction is a distinct kind of object that is not itself an instance of the abstraction. In practice, these two views of object abstraction are very closely related/ For example, a very simple addition to the above definition essentially turns Point into a "function exemplar". Function exemplars can also be thought of as "class exemplars":

let Point = { x:0, y,0, constructor(x,y} { this.x=x; this.y=y; } }.constructor; //<----------- note added property reference

let p = new Point(1,2); //new operator applied to a constructible function

However, people that prefer the "classical" style will reasonably point out that it is both easy to forget the trailing property accessor in this definition and that even if it is there a reader is likely to not notice it.

This proposal provides a solution to these and other related issues that allow object exemplars and class/function exemplars to have equivalent status and to be built upon the same underlying syntax and semantic mechanisms.

The class Operator

The class operator is used in a new variant of UnaryExpresson:

UnaryExpression : class UnaryExpression ...

The semantics are:

  1. if UnaryExpression is undefined or null, return the value of UnaryExpression.
  2. Let obj be ToObject(UnaryExpression)
  3. Return the result of calling the [[Get]] internal method of obj with argument 'constructor'

Using the class Operator

Prefixing an object exemplar with the class operator turns it into a class exemplar:

let Point = class { x:0, y,0, constructor(x,y} { this.x=x; this.y=y; } };

The precedence of the class operator and the semantics of <| means that it also works as expected with object exemplars that specify an inheritance relationship:

let Point = class AbstractPoint <| { x:0, y,0, constructor(x,y} { this.x=x; this.y=y; } };

In the above example Point inherits from AbstractPoint. If AbstractPoint is a function then this means that the [[Prototype]] of Point is AbstractPoint and that the [[Prototype]] of Point.prototype is AbstractPoint.prototype. If AbstractionPoint is an object exemplar it means that the [[Prototype]] of Point is AbstractPoint.constructor and that the [[Prototype]] of Point.prototype is AbstractPoint.

The class operator can prefix any UnaryExpression, not just object literals. That means that the class operator can be used to classify objects:

if (class p === Point) ...

Note that many object-oriented abstraction designers consider this for of class testing to be an anti-pattern.

It also works with instances of the standard ES built-in constructors:

if (class new Array(1,2,3,4) === Array) ... //this will be true

so are these:

if (class [1,2,3,4] === Array) ... if (class [].pop === Function) ...

It also works with private value types:

if (class 1234 === Number && class false === Boolean && class "Hi" === String) ... //all true

but note that

if (class null === Object) ...

is false.

You can use the class operator to determine if two objects are instances of the same exemplar:

if (class p === class q) ...

Note that this works for instances of both class and object exemplars. However, if you want to test if an object is an instance of a specific named object exemplar then you need to remember that such exemplars are prototype instances rather than constructor functions. Of course, if you prefer object exemplars you are probably used to thinking in that prototypal style. You would express this as:

if (class p === class Point) ... //for this example, assume the very first definition of Point

Finally, you might use class to create a new instance of the same exemplar as some other known object:

let mine = new class theirs;

This works the same regardless of whether the value of "theirs" is an object or class exemplar.

Relationship between the class and instanceof operators

There isn't one. They are different operators. class is simply a short hand for accessing an object's constructor property instanceof tests a specific inheritance relationship.

# Brendan Eich (14 years ago)

Quick reply to say two things:

  1. I like the more radical approach (need to ruminate on it more). Usability remains my concern for classes as sugar (the Cordova edition).

  2. Credit to Russell Leggett for posting this idea (I'm pretty sure) as

esdiscuss/2011-November/017986

# Axel Rauschmayer (14 years ago)
  1. I like the more radical approach (need to ruminate on it more). Usability remains my concern for classes as sugar (the Cordova edition).

  2. Credit to Russell Leggett for posting this idea (I'm pretty sure) as

esdiscuss/2011-November/017986

Sorry to be petty about this – I suggested it in June:

esdiscuss/2011-June/015084

# Axel Rauschmayer (14 years ago)

Never mind – I misread both Allen’s email and Russel’s gist.

Sorry!

Axel

# Axel Rauschmayer (14 years ago)

This is the middle ground between class proponents and object exemplar proponents, right?

  • Object exemplar proponents get object literal syntax, but will still have to use MyClass.prototype (e.g. to add methods) after having declared a class.

  • Class proponents get syntax that looks like some of the class declaration proposals, but don’t get a “proper” class construct.

Given that backward compatibility is a major issue, the proposal sounds reasonable. It also nicely settles the syntax debates surrounding class declarations. As “class declaration light”, it probably makes neither party completely happy, but maybe that’s what is called for.

# Axel Rauschmayer (14 years ago)

The class operator can prefix any UnaryExpression, not just object literals. That means that the class operator can be used to classify objects:

if (class p === Point) ...

Note that many object-oriented abstraction designers consider this for of class testing to be an anti-pattern.

It also works with instances of the standard ES built-in constructors:

if (class new Array(1,2,3,4) === Array) ... //this will be true

so are these:

if (class [1,2,3,4] === Array) ... if (class [].pop === Function) ...

Is it a good idea to overload this operator?

  1. class objectExemplar: “turn the objectExemplar into a class”
  2. class obj: “get the class of obj”

I would give operator #2 the name classof and let class only return the own property value of constructor.

# Brendan Eich (14 years ago)

On Nov 14, 2011, at 12:35 PM, Axel Rauschmayer wrote:

  1. I like the more radical approach (need to ruminate on it more). Usability remains my concern for classes as sugar (the Cordova edition).

  2. Credit to Russell Leggett for posting this idea (I'm pretty sure) as

esdiscuss/2011-November/017986

Sorry to be petty about this – I suggested it in June:

esdiscuss/2011-June/015084

Sorry, I don't see class as the dual of new, or class as an operator there.

True, others have previously proposed Class(...) or other helper functions. I'm not saying Russell came first, but I was struck by his post, meant to reply to it, and now see it in Allen's writeup. Thanks, Russell!

# Axel Rauschmayer (14 years ago)
  1. Credit to Russell Leggett for posting this idea (I'm pretty sure) as

esdiscuss/2011-November/017986

Sorry to be petty about this – I suggested it in June:

esdiscuss/2011-June/015084

Sorry, I don't see class as the dual of new, or class as an operator there.

True, others have previously proposed Class(...) or other helper functions. I'm not saying Russell came first, but I was struck by his post, meant to reply to it, and now see it in Allen's writeup. Thanks, Russell!

Yes, I feel embarrassed about that. I thought it was about the idea of object exemplars, but I’ve completely misread both Russell’s gist and Allen’s email.

# Brendan Eich (14 years ago)

On Nov 14, 2011, at 12:16 PM, Allen Wirfs-Brock wrote:

let Point = { x:0, y,0, constructor(x,y} { this.x=x; this.y=y; } }.constructor; //<----------- note added property reference

let p = new Point(1,2); //new operator applied to a constructible function

So can you spec operator new a bit? It tries [[Construct]] and if missing, tries for a .constructor property that it calls on a fresh instance of Object whose [[Prototype]] is the exemplar?

Dave's work on strawman:minimal_classes brought to light some added wiring that comes for free in the prototypal pattern when you write a constructor function as a function declaration: the intrinsic name of the constructor function is the class name.

For exemplars, if constructor is defined using method definition shorthand,

harmony:object_literals#object_literal_property_shorthands

says the intrinsic name will be 'constructor'. This will be a drag in debugging scenarios. You'll want to see Point, Square, Circle, Ellipse, Rectangle, etc. but all you'll get is 'constructor' every time.

The attributes in the harmony:object_literals#object_literal_property_shorthands section are frosty. Is that the right default for methods? Is it the right one for constructor in particular? I'm just asking, this is a separate issue but I spotted it and wanted to get it out before forgetting.

UnaryExpression : class UnaryExpression ...

The semantics are:

  1. if UnaryExpression is undefined or null, return the value of UnaryExpression.
  2. Let obj be ToObject(UnaryExpression)
  3. Return the result of calling the [[Get]] internal method of obj with argument 'constructor'

Interesting, so 'constructor' will inherit from Object.prototype if missing from the exemplar. This means

let Point = class { x:0, y,0 };

let p = new Point(1, 2);

will result in p being constructed via the equivalent of new Number(1).

The class operator can prefix any UnaryExpression, not just object literals. That means that the class operator can be used to classify objects:

if (class p === Point) ...

This is good.

It has the same multiple-global issue that instanceof has.

Note that many object-oriented abstraction designers consider this for of class testing to be an anti-pattern.

Yup but it has its uses occasionally.

Relationship between the class and instanceof operators

There isn't one. They are different operators. class is simply a short hand for accessing an object's constructor property instanceof tests a specific inheritance relationship.

Oh, but weren't you going to make instanceof work with an object exemplar on the right?

# Allen Wirfs-Brock (14 years ago)

On Nov 14, 2011, at 12:28 PM, Brendan Eich wrote:

Quick reply to say two things:

  1. I like the more radical approach (need to ruminate on it more). Usability remains my concern for classes as sugar (the Cordova edition).

  2. Credit to Russell Leggett for posting this idea (I'm pretty sure) as

esdiscuss/2011-November/017986

cool. I don't think I saw Russell's post or if I did I didn't find it notable because I had been thinking about this approach for awhile. But it's good to see that more than one person has discovered this approach lurking in the froth.

# Allen Wirfs-Brock (14 years ago)

On Nov 14, 2011, at 1:06 PM, Axel Rauschmayer wrote:

This is the middle ground between class proponents and object exemplar proponents, right?

Yes, next up is to write-up a stawman that ties all the pieces together into a rational story/

  • Object exemplar proponents get object literal syntax, but will still have to use MyClass.prototype (e.g. to add methods) after having declared a class.

or they just use the pure object exemplar pattern with the class operator:

let Point = { x:0, y,0, offsetBy(aPoint) {return new Point(this.x+aPoint.x, this.y+aPoint.y}, //<---- a method constructor(x,y} { this.x=x; this.y=y; } };

  • Class proponents get syntax that looks like some of the class declaration proposals, but don’t get a “proper” class construct.

I would say that we are avoiding adding any new declarations (other than let and const which are really all we need);

Given that backward compatibility is a major issue, the proposal sounds reasonable. It also nicely settles the syntax debates surrounding class declarations. As “class declaration light”, it probably makes neither party completely happy, but maybe that’s what is called for.

We can't make ES into a pure class-based language such as Dart (and many wouldn't want to) so whatever we do has to place well with what it already there and also the various different abstraction styles that different sub-communities have developed. Hopefully we can get past these debates an provide a good solution that uniquely builds upon the deep foundations JavaScript

# Allen Wirfs-Brock (14 years ago)

On Nov 14, 2011, at 1:15 PM, Axel Rauschmayer wrote:

The class operator can prefix any UnaryExpression, not just object literals. That means that the class operator can be used to classify objects:

if (class p === Point) ...

Note that many object-oriented abstraction designers consider this for of class testing to be an anti-pattern.

It also works with instances of the standard ES built-in constructors:

if (class new Array(1,2,3,4) === Array) ... //this will be true

so are these:

if (class [1,2,3,4] === Array) ... if (class [].pop === Function) ...

Is it a good idea to overload this operator?

  1. class objectExemplar: “turn the objectExemplar into a class”
  2. class obj: “get the class of obj”

I would give operator #2 the name classof and let class only return the own property value of constructor.

I'm not over-loading the class operator. I the above I was simply pointing out that this is what the operator I defined does in those circumstances. The concept of a constructor function being the "class" of an object is one of those things that has been around JS apparently from the beginning. It just didn't have a suggestively named operator. However an object is instantiated, this operator is simply reporting that the "class"of the object whatever is accessible as the object's "constructor".

# Brendan Eich (14 years ago)

On Nov 14, 2011, at 2:24 PM, Allen Wirfs-Brock wrote:

On Nov 14, 2011, at 1:15 PM, Axel Rauschmayer wrote:

if (class [1,2,3,4] === Array) ... if (class [].pop === Function) ...

Is it a good idea to overload this operator?

  1. class objectExemplar: “turn the objectExemplar into a class”
  2. class obj: “get the class of obj”

These are one and the same operator, as Allen says.

I would give operator #2 the name classof and let class only return the own property value of constructor.

I'm not over-loading the class operator. I the above I was simply pointing out that this is what the operator I defined does in those circumstances. The concept of a constructor function being the "class" of an object is one of those things that has been around JS apparently from the beginning. It just didn't have a suggestively named operator. However an object is instantiated, this operator is simply reporting that the "class"of the object whatever is accessible as the object's "constructor".

I'm really warming up to this radical class-as-unary-get-the-constructor operator.

But, I think the missing 'constructor' method bug will bite people. Should class throw if there is no own 'constructor' property? Mutating is out, and copying is out too.

# Waldemar Horwat (14 years ago)

On 11/14/2011 12:16 PM, Allen Wirfs-Brock wrote:

/UnaryExpression/ : class /UnaryExpression/ ...

The semantics are:

  1. if /UnaryExpression/ is undefined or null, return the value of /UnaryExpression/.
  2. Let /obj/ be ToObject(/UnaryExpression/)
  3. Return the result of calling the [[Get]] internal method of /obj/ with argument 'constructor'

Using the class Operator

Prefixing an object exemplar with the class operator turns it into a class exemplar:

let Point = class { x:0, y,0, constructor(x,y} { this.x=x; this.y=y; } };

I don't understand what you're accomplishing here. The above appears to be equivalent (modulo the typos) to:

let Point = function(x, y) { this.x = x; this.y = y; };

The other stuff inside the class is ignored.

 Waldemar
# Allen Wirfs-Brock (14 years ago)

On Nov 14, 2011, at 2:01 PM, Brendan Eich wrote:

On Nov 14, 2011, at 12:16 PM, Allen Wirfs-Brock wrote:

let Point = { x:0, y,0, constructor(x,y} { this.x=x; this.y=y; } }.constructor; //<----------- note added property reference

let p = new Point(1,2); //new operator applied to a constructible function

So can you spec operator new a bit? It tries [[Construct]] and if missing, tries for a .constructor property that it calls on a fresh instance of Object whose [[Prototype]] is the exemplar?

Yup, that's essentially the semantics. I'm going to write up the object exemplar strawman which will cover this (and also instanceof).

Dave's work on strawman:minimal_classes brought to light some added wiring that comes for free in the prototypal pattern when you write a constructor function as a function declaration: the intrinsic name of the constructor function is the class name.

For exemplars, if constructor is defined using method definition shorthand,

harmony:object_literals#object_literal_property_shorthands

says the intrinsic name will be 'constructor'. This will be a drag in debugging scenarios. You'll want to see Point, Square, Circle, Ellipse, Rectangle, etc. but all you'll get is 'constructor' every time.

This is an interesting point that arises for any non-binding declaration form. Function expression really are a similar situation where the only (semantic) reason for having a name is so it can be intrinsically referenced. In theory, some sort of internal naming syntax could be adopted for this case. However, I worry that that part leads to more and more embellishment that try to address different minor use cases and usability issues. We eventually get back to Cordova proposals that we can't get consensus on. I tend to think of JS as being more of a non-builtout OEM VW bus rather than a 70's Detroit luxury car.

Another approach to the debugging problem is the one put forward in the Mirghasem, et al SPLASH/Wavefront paper (dl.acm.org/citation.cfm?id=2048222&preflayout=tabs preprint: blog.getfirebug.com/2011/04/28/naming-anonymous-javascript-functions ) Such constructors would simply be recognized as yet another variation of implicit function naming that debuggers should be able to recognize (perhaps with some front-end help) and assign a meaningful name to. It's probably a win if tools can deal with it out having to add to the language.

The attributes in the harmony:object_literals#object_literal_property_shorthands section are frosty. Is that the right default for methods? Is it the right one for constructor in particular? I'm just asking, this is a separate issue but I spotted it and wanted to get it out before forgetting.

We've thrashed around about attributes for concise methods with the latest decision happening at the Sept. TC39 meeting. I don't know if those decisions are still holding. Regardless, I think "constructor" should be considered special and hence potentially could have other attribute defaults. Another special need of constructor method (in an object literal) is that it needs to have its prototype property automatically set to the object that contains it. (another detail for the object exemplar strawman).

# Axel Rauschmayer (14 years ago)

Is it a good idea to overload this operator?

  1. class objectExemplar: “turn the objectExemplar into a class”
  2. class obj: “get the class of obj”

I would give operator #2 the name classof and let class only return the own property value of constructor.

I'm not over-loading the class operator. I the above I was simply pointing out that this is what the operator I defined does in those circumstances.

OK. Overloading might have been the wrong term – it is the same implementation. I meant semantic overloading: two semantically different roles, performed by the same operator (see below).

The concept of a constructor function being the "class" of an object is one of those things that has been around JS apparently from the beginning. It just didn't have a suggestively named operator. However an object is instantiated, this operator is simply reporting that the "class"of the object whatever is accessible as the object's "constructor".

So you don’t think the two roles clash semantically? (1) “Turn an object exemplar into a class” versus (1) “get the class of an object”. Both are definitely useful! But that they can be performed by the same operator seems like a happy coincidence. If (1) was to be further refined, e.g., as Brendan suggested, by throwing an exception if an object exemplar does not have a "constructor" method then it seems both roles would drift further apart. I’m mainly arguing for a clear separation of concerns.

# David Flanagan (14 years ago)

On 11/14/11 3:01 PM, Allen Wirfs-Brock wrote:

On Nov 14, 2011, at 2:01 PM, Brendan Eich wrote:

On Nov 14, 2011, at 12:16 PM, Allen Wirfs-Brock wrote:

let Point = { x:0, y,0, constructor(x,y} { this.x=x; this.y=y; } }.constructor; //<----------- note added property reference

let p = new Point(1,2); //new operator applied to a constructible function

So can you spec operator new a bit? It tries [[Construct]] and if missing, tries for a .constructor property that it calls on a fresh instance of Object whose [[Prototype]] is the exemplar?

Yup, that's essentially the semantics. I'm going to write up the object exemplar strawman which will cover this (and also instanceof).

I have a bad feeling about making 'new' work with both functions and object exemplars. If we can have two different types of classes, we're going to end up using typeof on our class objects to figure out what kind of class they are. If I've got a value C from a library and I think it is an object exemplar, but it is in fact a constructor function, then 'class C' is going to return Function rather than C itself...

Object exemplars does not pave the cowpaths for classes: it creates a new path and just smears out the concept of a class into something more broad. I fear it will create confusion rather than " provide a terse and declarative surface for those semantics so that programmer intent is expressed instead of the underlying imperative machinery."

# Allen Wirfs-Brock (14 years ago)

On Nov 14, 2011, at 2:53 PM, Waldemar Horwat wrote:

On 11/14/2011 12:16 PM, Allen Wirfs-Brock wrote:

/UnaryExpression/ : class /UnaryExpression/ ...

The semantics are:

  1. if /UnaryExpression/ is undefined or null, return the value of /UnaryExpression/.
  2. Let /obj/ be ToObject(/UnaryExpression/)
  3. Return the result of calling the [[Get]] internal method of /obj/ with argument 'constructor'

Using the class Operator

Prefixing an object exemplar with the class operator turns it into a class exemplar:

let Point = class { x:0, y,0, constructor(x,y} { this.x=x; this.y=y; } };

I don't understand what you're accomplishing here. The above appears to be equivalent (modulo the typos) to:

let Point = function(x, y) { this.x = x; this.y = y; };

The other stuff inside the class is ignored.

I'm gong to add an method to emphasize an implicit point and fix a typo or two (hopefully more than I create). A desugaring of:

let Point = class { x:0, y:0, offsetBy(aPoint) { return new class this(this.x+aPoint.x, this.y+aPoint.y); }, constructor(x,y} { this.x=x; this.y=y; } };

into ES5(+let) is:

let Point = function(x,y) { this.x=x; this.y=y; }; Point.prototype.x = 0; Point.prototype.y = 0; Point.prototype.offsetBy = function(aPoint) { return new this.constructor(this.x+aPoint.x, this.y+aPoint.y}; }

without the class operator in the let initializer it would turn into

let Point = { x:0, y:0, offsetBy: function(aPoint) { return new this.constructor(this.x+aPoint.x, this.y+aPoint.y}, constructor: function(x,y} { this.x=x; this.y=y; } }; Point.constructor.prototype=Point;

all the same pieces, just assembled slightly differently

# Allen Wirfs-Brock (14 years ago)

On Nov 14, 2011, at 3:09 PM, Axel Rauschmayer wrote:

The concept of a constructor function being the "class" of an object is one of those things that has been around JS apparently from the beginning. It just didn't have a suggestively named operator. However an object is instantiated, this operator is simply reporting that the "class"of the object whatever is accessible as the object's "constructor".

So you don’t think the two roles clash semantically? (1) “Turn an object exemplar into a class” versus (1) “get the class of an object”. Both are definitely useful! But that they can be performed by the same operator seems like a happy coincidence. If (1) was to be further refined, e.g., as Brendan suggested, by throwing an exception if an object exemplar does not have a "constructor" method then it seems both roles would drift further apart. I’m mainly arguing for a clear separation of concerns.

The two roles are the same! The value of the constructor property of an object exemplar is a function exemplar (AKA class exemplar) whose prototype property's value is the object exemplar. When you say: let P = class {constructor() {} }; You aren't turning an object exemplar into a class exemplar your are simply accessing the class exemplar that is explicitly defined as part of the object exemplar.

The above let is also equivalent to:

let PrototypalP = {constructor(){} }; let P = class PrototypalP;

which is equivalent to:

let P = class {constructor(){} }; let PrototypalP = P.prototype;

Finally, when I talk about an object's constructor I include the possibility that 'constructor' is inherited, so the above identifies only hold when an own constructor property is explicitly provided. In fact, that is really what I mean by an object exemplar

var foo = { }; class foo is Object; //true Object.prototype is foo; //also true

bar = new foo; //same as new foo.constructor which is the same as new Object class bar is class foo; //and both === Object.

It all falls out of the plumbing.

# Jake Verbaten (14 years ago)

UnaryExpression : class UnaryExpression ...

The semantics are:

  1. if UnaryExpression is undefined or null, return the value of * UnaryExpression*.
  2. Let obj be ToObject(UnaryExpression)
  3. Return the result of calling the [[Get]] internal method of obj with argument 'constructor'

Interesting, so 'constructor' will inherit from Object.prototype if missing from the exemplar. This means

let Point = class { x:0, y,0 };

let p = new Point(1, 2);

will result in p being constructed via the equivalent of new Number(1).

When you create a function, it has an automatically created prototype object who has a constructor property defaulted to the original function.

Why would it be bad to augment the UnaryExpression after the class keyword so that it has an empty constructor if it doesn't exist?

# Jake Verbaten (14 years ago)

The whole concept is great.

What class does for the JS community is give everyone a standardized way to write "classes". The simplest way to have a class is to call ObjectLiteral.constructor and that's exactly what this class syntax does.

This also means that it's very easy to adapt to using this.

This class proposal is also orthogonal to object exemplars.

Apart from setting obj.constructor.prototype = obj in the background when you declare an object literal and making function <| object work, we don't actually need to augment new and instanceof to work with objects.

On Mon, Nov 14, 2011 at 8:16 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

(I'm going to write this up as a strawman, this list gets first shot at it...)

Introduction

[snip]

# Allen Wirfs-Brock (14 years ago)

On Nov 14, 2011, at 3:15 PM, David Flanagan wrote:

On 11/14/11 3:01 PM, Allen Wirfs-Brock wrote:

On Nov 14, 2011, at 2:01 PM, Brendan Eich wrote:

On Nov 14, 2011, at 12:16 PM, Allen Wirfs-Brock wrote:

let Point = { x:0, y,0, constructor(x,y} { this.x=x; this.y=y; } }.constructor; //<----------- note added property reference

let p = new Point(1,2); //new operator applied to a constructible function

So can you spec operator new a bit? It tries [[Construct]] and if missing, tries for a .constructor property that it calls on a fresh instance of Object whose [[Prototype]] is the exemplar?

Yup, that's essentially the semantics. I'm going to write up the object exemplar strawman which will cover this (and also instanceof).

I have a bad feeling about making 'new' work with both functions and object exemplars. If we can have two different types of classes, we're going to end up using typeof on our class objects to figure out what kind of class they are. If I've got a value C from a library and I think it is an object exemplar, but it is in fact a constructor function, then 'class C' is going to return Function rather than C itself...

Well, new'ing object exemplars has always been the central concept of our discussions about them. Essentially it is the self style of object creation and arguably the way the prototypal instantiation is supposed to work. It seems to be what people who really like prototypal inheritance really want to do.

The "magic" of what I'm proposing is that you will rarely have to worry about whether a named object abstraction (call it Foo) you get from a library is an object exemplar or a function/class exemplar. Without knowing you can say: new Foo(args) and it will work. Without knowing you can do either : let subfoo = Foo <| {constructor() {}}; or let subfoo = class Foo <| {constructor() {}} //note this parses as: class (Foo <| {... }) and things will work. You can even say: let subfoo = Foo <| function() {} and the right thinks happen.

To me, this seems like the essence of object-oreinted implementation encapsulation. All a client of Foo needs to know is that Foo is an "exemplar" and hence it can be instantiated (via new) or specialized (via <|). It is up to the implementor of Foo to decide how to best express its implementation. They can even change their mind and clients shouldn't care.

Finally, if you really need to know whether an exemplar is an object exemplar or a function exemplar then typeof Foo is Function seems like a fine way to test it. (I'm really growing to like the is operator...)

Object exemplars does not pave the cowpaths for classes: it creates a new path and just smears out the concept of a class into something more broad. I fear it will create confusion rather than " provide a terse and declarative surface for those semantics so that programmer intent is expressed instead of the underlying imperative machinery."

I look at it from the perspective that we already have at least two cowpaths that meander across each other in ways that make it hard to stay on either one. I'm optimistic that we pave them in such a way that we are really dealing so parallel lanes on the freeway (and this analogy just got totally out of hand).

# Axel Rauschmayer (14 years ago)

So you don’t think the two roles clash semantically? (1) “Turn an object exemplar into a class” versus (2) “get the class of an object”. Both are definitely useful! But that they can be performed by the same operator seems like a happy coincidence. If (1) was to be further refined, e.g., as Brendan suggested, by throwing an exception if an object exemplar does not have a "constructor" method then it seems both roles would drift further apart. I’m mainly arguing for a clear separation of concerns.

The two roles are the same!

=== Role 1 ===

The value of the constructor property of an object exemplar is a function exemplar (AKA class exemplar) whose prototype property's value is the object exemplar. When you say: let P = class {constructor() {} }; You aren't turning an object exemplar into a class exemplar your are simply accessing the class exemplar that is explicitly defined as part of the object exemplar.

The above let is also equivalent to:

let PrototypalP = {constructor(){} }; let P = class PrototypalP;

which is equivalent to:

let P = class {constructor(){} }; let PrototypalP = P.prototype;

=== Role 2 ===

Finally, when I talk about an object's constructor I include the possibility that 'constructor' is inherited, so the above identifies only hold when an own constructor property is explicitly provided. In fact, that is really what I mean by an object exemplar

var foo = { }; class foo is Object; //true ??? Object.prototype is foo; //also true

bar = new foo; //same as new foo.constructor which is the same as new Object class bar is class foo; //and both === Object.

It all falls out of the plumbing.

That makes sense (apart from the line marked with ???).

I like to have a human language, intuitive description for what an operator does. How about the following one for class?

 “Retrieve the class (function exemplar) of the operand”
  or “go to the nearest class” [in the case of an object exemplar, the nearest class is inside the exemplar]

But the description is not “determine the constructor function that created the operand”.

# David Flanagan (14 years ago)

On 11/14/11 4:16 PM, Allen Wirfs-Brock wrote:

On Nov 14, 2011, at 3:15 PM, David Flanagan wrote:

I have a bad feeling about making 'new' work with both functions and object exemplars. If we can have two different types of classes, we're going to end up using typeof on our class objects to figure out what kind of class they are. If I've got a value C from a library and I think it is an object exemplar, but it is in fact a constructor function, then 'class C' is going to return Function rather than C itself... Well, new'ing object exemplars has always been the central concept of our discussions about them. Essentially it is the self style of object creation and arguably the way the prototypal instantiation is supposed to work. It seems to be what people who really like prototypal inheritance really want to do.

Apparently I wasn't paying attention to the early discussions about object exemplars. I've heard the term used, but missed the point about changing the behavior of new. It seems to me people who want to use self-style object creation can use Object.create() and people who want to use JavaScript-style object creation can use new like we've been doing for 15 years. (Just today I wrote a blog post explaining why I'm hoping for classes in ES.next, and included, as part of my argument, the assertion that all the proposals on the table are just syntax sugar without new language semantics. I was wrong about that, I guess!)

The "magic" of what I'm proposing is that you will rarely have to worry about whether a named object abstraction (call it Foo) you get from a library is an object exemplar or a function/class exemplar. Without knowing you can say: new Foo(args)

Maybe I'm overreacting, and new semantics can make this all just work.
But it seems like a major, major change to the language.

and it will work. Without knowing you can do either : let subfoo = Foo<| {constructor() {}}; or let subfoo = class Foo<| {constructor() {}} //note this parses as: class (Foo<| {... }) and things will work. You can even say: let subfoo = Foo<| function() {} and the right thinks happen.

To me, this seems like the essence of object-oreinted implementation encapsulation. All a client of Foo needs to know is that Foo is an "exemplar" and hence it can be instantiated (via new) or specialized (via<|). It is up to the implementor of Foo to decide how to best express its implementation. They can even change their mind and clients shouldn't care.

Do JavaScript programmers want exemplars or do they want classes?

Finally, if you really need to know whether an exemplar is an object exemplar or a function exemplar then typeof Foo is Function seems like a fine way to test it. (I'm really growing to like the is operator...)

Object exemplars does not pave the cowpaths for classes: it creates a new path and just smears out the concept of a class into something more broad. I fear it will create confusion rather than " provide a terse and declarative surface for those semantics so that programmer intent is expressed instead of the underlying imperative machinery." I look at it from the perspective that we already have at least two cowpaths that meander across each other in ways that make it hard to stay on either one. I'm optimistic that we pave them in such a way that we are really dealing so parallel lanes on the freeway (and this analogy just got totally out of hand).

If I may continue the out-of-hand metaphor, I worry that we'll end up with a two lane freeway that is being repaved and has "Warning: abrupt edge!" signs because one lane is bumpy and 2 inches lower than the other lane.

# Allen Wirfs-Brock (14 years ago)

On Nov 14, 2011, at 4:05 PM, Jake Verbaten wrote:

UnaryExpression : class UnaryExpression ...

The semantics are:

  1. if UnaryExpression is undefined or null, return the value of UnaryExpression.
  2. Let obj be ToObject(UnaryExpression)
  3. Return the result of calling the [[Get]] internal method of obj with argument 'constructor'

Interesting, so 'constructor' will inherit from Object.prototype if missing from the exemplar. This means

let Point = class { x:0, y,0 };

let p = new Point(1, 2);

will result in p being constructed via the equivalent of new Number(1).

sh*t! I though I'd had a solution for this, but maybe I don't...

If you are really going to define either an object or class exemplar, you really need to define a constructor. The object exemplar case actually works out ok in this case because the inherited constructor is "called as a function", not "as a constructor". So for

let Point = { x: 0, y: 0 }; let p = new Point(1,2);

Is pretty much equivalent to: let p = Point <| {};

which may not be exactly what the programmer expected but it is a least in the vicinity and yields an object with (inherited) x and y properties.

When you create a function, it has an automatically created prototype object who has a constructor property defaulted to the original function.

Why would it be bad to augment the UnaryExpression after the class keyword so that it has an empty constructor if it doesn't exist?

that would turn class from a simple property accessing operator to a mutating operator that I'm sure would never be accepted.

A pattern like: class { } should always have a constructor specified in the object literal but saying the class operator (as I'm trying to define it) adds a constructor seems very unhygienic. Of course, it would be great if my IDE, or even my ES engine warned me when I coded this. Maybe that's enough. But I suspect that it isn't. I'm going to have to put some more thought into the own vs inherited constructor part of the design.

# Mark S. Miller (14 years ago)

On Mon, Nov 14, 2011 at 2:32 PM, Brendan Eich <brendan at mozilla.com> wrote:

I'm really warming up to this radical class-as-unary-get-the-constructor operator.

Likewise.

But, I think the missing 'constructor' method bug will bite people. Should class throw if there is no own 'constructor' property? Mutating is out, and copying is out too.

But, when we're testing a value rather than constructing a class, I think we're getting it wrong to think of "class x" as being sugar, or even approximately sugar, for "x.constructor". If x is simply an instance, it may well have an own property named "constructor" that has nothing to do with the class-like pattern. Better would be for it to be (approximately) sugar for "Object.getPrototypeOf(x).constructor" (or more accurately, in spec language, "x.[[Prototype]].constructor". But this isn't really right either. I think the meaning of "class x" in this proposal should produce the following invariant:

x instanceof class x

This suggests the following executable spec, which is only reasonable if it can be optimized to almost nothing in the typical case:

function [[GetClass]](x) {
  while (true) {
    const proto = Object.getPrototypeOf(x);
    if (proto === null) { return null; /* or void 0? */ }
    let result;
    if (hasOwnProperty(proto, 'constructor') &&
        (result = proto.constructor) &&
        typeof result === 'function' &&
        result.prototype === proto) {
      return result;
    }
  }
}

This does not guarantee no false positives, but since we're just trying to (duck) recognize a pattern, we can't. But the above definition will avoid almost all false positives, and in does guarantee the instanceof implication.

Many JS programmers (including myself) do not initialize, for example, a Point.prototype to be a valid (prototypical) point. And I think we are generally right not to do so. Thus, the fact that "!(Point.prototype instanceof Point)" in JavaScript is a virtue, not a flaw of the language. We treat Point.prototype effectively as a form of vtable, providing just the things that all points share, not the things that should be initialized per point. Since, for us, Point.prototype is not a kind of Point, it would be a terrible mistake if "class Point.prototype === Point".

Much existing code that engages in prototypal encoding on the class pattern remembers to hook up the constructor. And much forgets. Given an instance x which effectively inherits from a legacy Foo "class", which forgot to hook up constructor, which "subclasses" a Bar "class" which does, there is no way to recover the Foo constructor function from x. The best one can do is recover the Bar constructor function, which the spec above does.

Perhaps this hit-or-miss nature of "constructor" can become a virtue. Given the need that Allen separately mentioned for a species-like notion, paralleling "species" in Smalltalk, "new class this(...)" would make a new instance of the same species as this.

Given this meaning of "class" when testing a value, I'm not sure if we can rescue the pun above by which we use the same operator to define a class. If forced to choose only one of these two roles for the "class" keyword, I'd prefer to use it only to define a class, not to test values. But having only just noticed this discrepancy, I am also not sure we cannot rescue the pun. Let's try.

# Allen Wirfs-Brock (14 years ago)

On Nov 14, 2011, at 4:50 PM, David Flanagan wrote:

On 11/14/11 4:16 PM, Allen Wirfs-Brock wrote:

On Nov 14, 2011, at 3:15 PM, David Flanagan wrote:

I have a bad feeling about making 'new' work with both functions and object exemplars. If we can have two different types of classes, we're going to end up using typeof on our class objects to figure out what kind of class they are. If I've got a value C from a library and I think it is an object exemplar, but it is in fact a constructor function, then 'class C' is going to return Function rather than C itself... Well, new'ing object exemplars has always been the central concept of our discussions about them. Essentially it is the self style of object creation and arguably the way the prototypal instantiation is supposed to work. It seems to be what people who really like prototypal inheritance really want to do. Apparently I wasn't paying attention to the early discussions about object exemplars. I've heard the term used, but missed the point about changing the behavior of new. It seems to me people who want to use self-style object creation can use Object.create() and people who want to use JavaScript-style object creation can use new like we've been doing for 15 years. (Just today I wrote a blog post explaining why I'm hoping for classes in ES.next, and included, as part of my argument, the assertion that all the proposals on the table are just syntax sugar without new language semantics. I was wrong about that, I guess!)

Object create doesn't do the job because it doesn't provide for calling an "initialize" methods (turns out to be a very important part of the self-style).

The "magic" of what I'm proposing is that you will rarely have to worry about whether a named object abstraction (call it Foo) you get from a library is an object exemplar or a function/class exemplar. Without knowing you can say: new Foo(args) Maybe I'm overreacting, and new semantics can make this all just work. But it seems like a major, major change to the language.

being able to apply new to a non-function object is an addition, not a change. Up to this point, the feedback seems to have viewed it as a minor addition.

and it will work. Without knowing you can do either : let subfoo = Foo<| {constructor() {}}; or let subfoo = class Foo<| {constructor() {}} //note this parses as: class (Foo<| {... }) and things will work. You can even say: let subfoo = Foo<| function() {} and the right thinks happen.

To me, this seems like the essence of object-oreinted implementation encapsulation. All a client of Foo needs to know is that Foo is an "exemplar" and hence it can be instantiated (via new) or specialized (via<|). It is up to the implementor of Foo to decide how to best express its implementation. They can even change their mind and clients shouldn't care. Do JavaScript programmers want exemplars or do they want classes?

They seem to be split. Some vocally ask for better support for prototypal inheritance other vocally ask for classes.

I think what everybody is asking for is a better way to "created named object abstractions". The term exemplar is just one that I introduced into the discussion to make it easier to talk about the different kinds of entities you might apply such names to. In some language you name a "class declaration", in others you name a prototypal instance, in JS you have historically named a function. Exemplar was intended to be a generic for such "named object abstractions". An object exemplar is a self style prototype, a class exemplar is the sort of thing you find in languages llke Java. A function exemplar is a JS constructor,

Finally, if you really need to know whether an exemplar is an object exemplar or a function exemplar then typeof Foo is Function seems like a fine way to test it. (I'm really growing to like the is operator...)

Object exemplars does not pave the cowpaths for classes: it creates a new path and just smears out the concept of a class into something more broad. I fear it will create confusion rather than " provide a terse and declarative surface for those semantics so that programmer intent is expressed instead of the underlying imperative machinery." I look at it from the perspective that we already have at least two cowpaths that meander across each other in ways that make it hard to stay on either one. I'm optimistic that we pave them in such a way that we are really dealing so parallel lanes on the freeway (and this analogy just got totally out of hand). If I may continue the out-of-hand metaphor, I worry that we'll end up with a two lane freeway that is being repaved and has "Warning: abrupt edge!" signs because one lane is bumpy and 2 inches lower than the other lane.

certainly not the goal

# Mark S. Miller (14 years ago)

On Mon, Nov 14, 2011 at 5:13 PM, Mark S. Miller <erights at google.com> wrote:

function [[GetClass]](x) {
  while (true) {
    const proto = Object.getPrototypeOf(x);
    if (proto === null) { return null; /* or void 0? */ }
    let result;
    if (hasOwnProperty(proto, 'constructor') &&
        (result = proto.constructor) &&
        typeof result === 'function' &&
        result.prototype === proto) {
      return result;
    }
  }
}

Oops. Should be

function [[GetClass]](x) {
  while (true) {
    x = Object.getPrototypeOf(x);
    if (x === null) { return null; /* or void 0? */ }
    let result;
    if (hasOwnProperty(x, 'constructor') &&
        (result = x.constructor) &&
        typeof result === 'function' &&
        result.prototype === x) {
      return result;
    }
  }
}
# Brendan Eich (14 years ago)

On Nov 14, 2011, at 4:05 PM, Jake Verbaten <raynos2 at gmail.com> wrote:

When you create a function, it has an automatically created prototype object who has a constructor property defaulted to the original function.

Note that this unobservable mutation occurs before the function object is available to script.

Why would it be bad to augment the UnaryExpression after the class keyword so that it has an empty constructor if it doesn't exist?

Because UnaryExpression may evaluate to a reference to an existing object, in which the mutation of 'constructor' from not-own to defined is observable,

Mutation of operator operands apart from ++, +=, 'delete' and the like is a bad idea. Some object to .{ on this basis. 'class' unary prefix would be even more unusual.

# Allen Wirfs-Brock (14 years ago)

I think you're on the write track of the fix here. I knew I needed to specify something similar to make instanceof work reasonably for object exemplars. I need to look at it a bit more to be sure.

# Axel Rauschmayer (14 years ago)

Given this meaning of "class" when testing a value, I'm not sure if we can rescue the pun above by which we use the same operator to define a class. If forced to choose only one of these two roles for the "class" keyword, I'd prefer to use it only to define a class, not to test values. But having only just noticed this discrepancy, I am also not sure we cannot rescue the pun. Let's try.

Is a dual role even necessary? Why not stick to just class declaration? Looking through the eyes of an outsider, class { ... } looks like a class declaration. Given that we already have typeof and instanceof, wouldn’t an outsider expect a name such as classof? Furthermore, if object exemplars and function exemplars are to exist in parallel. Then:

 function Foo() {};
 var f = new Foo();
 classof f === Foo // true

 let Foo = {};
 var f = new Foo();
 classof f === Foo // true

The above would not hold for class.

# Brendan Eich (14 years ago)

On Nov 14, 2011, at 6:36 PM, Axel Rauschmayer wrote:

Given this meaning of "class" when testing a value, I'm not sure if we can rescue the pun above by which we use the same operator to define a class. If forced to choose only one of these two roles for the "class" keyword, I'd prefer to use it only to define a class, not to test values. But having only just noticed this discrepancy, I am also not sure we cannot rescue the pun. Let's try.

Is a dual role even necessary? Why not stick to just class declaration? Looking through the eyes of an outsider, class { ... } looks like a class declaration. Given that we already have typeof and instanceof, wouldn’t an outsider expect a name such as classof? Furthermore, if object exemplars and function exemplars are to exist in parallel. Then:

 function Foo() {};
 var f = new Foo();
 classof f === Foo // true

 let Foo = {};
 var f = new Foo();

This could succeed, due to 'constructor' inherited from Object.prototype. But, it could fail if we require an own constructor, or at least an inherited one whose value is a function object (or callable object if host) whose .prototpye is the exemplar.

 classof f === Foo // true

If new Foo() throws, you won't reach here, and all is consistent.

# Axel Rauschmayer (14 years ago)

Is a dual role even necessary? Why not stick to just class declaration? Looking through the eyes of an outsider, class { ... } looks like a class declaration. Given that we already have typeof and instanceof, wouldn’t an outsider expect a name such as classof? Furthermore, if object exemplars and function exemplars are to exist in parallel. Then:

 function Foo() {};
 var f = new Foo();
 classof f === Foo // true

 let Foo = {};
 var f = new Foo();

This could succeed, due to 'constructor' inherited from Object.prototype. But, it could fail if we require an own constructor, or at least an inherited one whose value is a function object (or callable object if host) whose .prototpye is the exemplar.

 classof f === Foo // true

If new Foo() throws, you won't reach here, and all is consistent.

Sorry, mucked it up (shouldn’t email when tired):

 let Foo = { constructor: function() {} }; // or any valid object exemplar on the RHS, really
 var f = new Foo();
 classof f === Foo // true
# Brendan Eich (14 years ago)

On Nov 14, 2011, at 6:47 PM, Axel Rauschmayer wrote:

Is a dual role even necessary? Why not stick to just class declaration? Looking through the eyes of an outsider, class { ... } looks like a class declaration. Given that we already have typeof and instanceof, wouldn’t an outsider expect a name such as classof? Furthermore, if object exemplars and function exemplars are to exist in parallel. Then:

 function Foo() {};
 var f = new Foo();
 classof f === Foo // true

 let Foo = {};
 var f = new Foo();

This could succeed, due to 'constructor' inherited from Object.prototype. But, it could fail if we require an own constructor, or at least an inherited one whose value is a function object (or callable object if host) whose .prototpye is the exemplar.

 classof f === Foo // true

If new Foo() throws, you won't reach here, and all is consistent.

Sorry, mucked it up (shouldn’t email when tired):

 let Foo = { constructor: function() {} }; // or any valid object exemplar on the RHS, really
 var f = new Foo();
 classof f === Foo // true

In that case class f === f.constructor && f.constructor === Foo per Allen's last post about this wiring.

# Brendan Eich (14 years ago)

On Nov 14, 2011, at 6:56 PM, Brendan Eich wrote:

On Nov 14, 2011, at 6:47 PM, Axel Rauschmayer wrote:

Sorry, mucked it up (shouldn’t email when tired):

 let Foo = { constructor: function() {} }; // or any valid object exemplar on the RHS, really
 var f = new Foo();
 classof f === Foo // true

In that case class f === f.constructor && f.constructor === Foo per Allen's last post about this wiring.

Maybe I'm over-optimistic. If you had written

let Foo = {constructor(){}};

then I could see wiring up Foo.constructor.prototype === Foo. But with the plain old property initaliser syntax, would we really require mutating .prototype to reference the enclosing object litetral?

# Dmitry Soshnikov (14 years ago)

On 15.11.2011 2:01, Brendan Eich wrote:

On Nov 14, 2011, at 12:16 PM, Allen Wirfs-Brock wrote:

let Point = { x:0, y,0, constructor(x,y} { this.x=x; this.y=y; } }.constructor; //<----------- note added property reference

let p = new Point(1,2); //new operator applied to a constructible function

So can you spec operator new a bit? It tries [[Construct]] and if missing, tries for a .constructor property that it calls on a fresh instance of Object whose [[Prototype]] is the exemplar?

Dave's work on strawman:minimal_classes brought to light some added wiring that comes for free in the prototypal pattern when you write a constructor function as a function declaration: the intrinsic name of the constructor function is the class name.

For exemplars, if constructor is defined using method definition shorthand,

harmony:object_literals#object_literal_property_shorthands

says the intrinsic name will be 'constructor'. This will be a drag in debugging scenarios. You'll want to see Point, Square, Circle, Ellipse, Rectangle, etc. but all you'll get is 'constructor' every time.

The attributes in the harmony:object_literals#object_literal_property_shorthands section are frosty. Is that the right default for methods? Is it the right one for constructor in particular? I'm just asking, this is a separate issue but I spotted it and wanted to get it out before forgetting.

/UnaryExpression/ : class /UnaryExpression/ ...

The semantics are:

  1. if /UnaryExpression/ is undefined or null, return the value of /UnaryExpression/.
  2. Let /obj/ be ToObject(/UnaryExpression/)
  3. Return the result of calling the [[Get]] internal method of /obj/ with argument 'constructor'

Sorry, perhaps I'm missing something (correct me if I'm wrong), but seems this algorithm just not working.

According to examples, yes, the Point is set to the function previously referred by temporary created object's `constructor' property. But what's then?

Instances created by such a constructor, will have prototype set to Object.prototype' (since it's the value ofPoint.prototype' now). Therefore, the do not see neither specified x' andy' default (shared, static) properties, nor any other method placed on that temp-object:

You may of course try to set Point.prototype = Point after that in a hope that it starts to see the properties, but it's obviously useless since Point already is set to temp-object's `constructor' property and actually is missed by GC.

Dmitry.

# Dmitry Soshnikov (14 years ago)

On 15.11.2011 11:50, Dmitry Soshnikov wrote:

On 15.11.2011 2:01, Brendan Eich wrote:

On Nov 14, 2011, at 12:16 PM, Allen Wirfs-Brock wrote:

let Point = { x:0, y,0, constructor(x,y} { this.x=x; this.y=y; } }.constructor; //<----------- note added property reference

let p = new Point(1,2); //new operator applied to a constructible function

So can you spec operator new a bit? It tries [[Construct]] and if missing, tries for a .constructor property that it calls on a fresh instance of Object whose [[Prototype]] is the exemplar?

Dave's work on strawman:minimal_classes brought to light some added wiring that comes for free in the prototypal pattern when you write a constructor function as a function declaration: the intrinsic name of the constructor function is the class name.

For exemplars, if constructor is defined using method definition shorthand,

harmony:object_literals#object_literal_property_shorthands

says the intrinsic name will be 'constructor'. This will be a drag in debugging scenarios. You'll want to see Point, Square, Circle, Ellipse, Rectangle, etc. but all you'll get is 'constructor' every time.

The attributes in the harmony:object_literals#object_literal_property_shorthands section are frosty. Is that the right default for methods? Is it the right one for constructor in particular? I'm just asking, this is a separate issue but I spotted it and wanted to get it out before forgetting.

/UnaryExpression/ : class /UnaryExpression/ ...

The semantics are:

  1. if /UnaryExpression/ is undefined or null, return the value of /UnaryExpression/.
  2. Let /obj/ be ToObject(/UnaryExpression/)
  3. Return the result of calling the [[Get]] internal method of /obj/ with argument 'constructor'

Sorry, perhaps I'm missing something (correct me if I'm wrong), but seems this algorithm just not working.

According to examples, yes, the Point is set to the function previously referred by temporary created object's `constructor' property. But what's then?

Instances created by such a constructor, will have prototype set to Object.prototype' (since it's the value ofPoint.prototype' now). Therefore, the do not see neither specified x' andy' default (shared, static) properties, nor any other method placed on that temp-object:

You may of course try to set Point.prototype = Point after that in a hope that it starts to see the properties, but it's obviously useless since Point already is set to temp-object's `constructor' property and actually is missed by GC.

Woops.. :) (I realized that I have 40 unread mails which I missed, so I guess it's very likely already was mentioned; sorry in case)

# Russell Leggett (14 years ago)

Sorry I'm late to the party, I'm just catching up now. I'm glad that what I proposed got mentioned (thanks Brendan), but I wanted to be clear that its not actually what Allen is proposing here. Where Allen is proposing a simple sugar operator to retrieve the constructor, I was proposing an operator that does effectively that in the simple case (object literal), but would create a new constructor function in the more complex case, and avoid some of the issues being brought up here. Let me go through my logic a little.

let Point = class {
     x:0,
     y,0,
     constructor(x,y} {
         this.x=x;
         this.y=y;
     }
};

In this example, Allen is proposing that object literals with a constructor property should automatically point back to the object with a .prototype property. I was assuming current behavior and that the class operator would perform this, but because it happens before the object is observable, no harm done.

Here is the crux of what I see as difficult with Allen's approach:

let Monster = class {
    constructor(name, health) {
        ...
    }
    attack(target) {
        log('The monster attacks ' + target);
    }
}

let BossMonster = class Monster <| {
    attack(target){
        log('Boss attacks ' + target);
    }
}

As has been said, a missing constructor would not result in the expected behavior. I can also imagine other scenarios where the behavior would be unusual.

function makeSpecialClass(obj){
    //perhaps we don't want to modify the original
    let sub = Object.create(obj);
    sub.newMethod = function(){...};
    return class sub;
}
...
let Specialized = makeSpecialClass(someObj);
...
//or what about
let f = new Foo();
f.newMethod = function(){...};
let Bar = class f;

I think this example is contrived, but illustrates the point that JavaScript is very dynamic, and sometimes in the building process an object can be out of sync from its constructor. My expectations when I see this would be that my results would be a constructor function that can be used to produce a new version of the UnaryExpression, calling its constructor code. The best way I can think of to do this would be:

function classify(obj){
    let sub = obj <| {
        constructor(...args){
            super.constructor(...args);
        }
    }
    sub.constructor.prototype = sub;
    return sub.constructor;
}

The key is that the UnaryExpression is not modified, and it should still work even if a constructor is missing. If the UnaryExpression is unobservable and had an own constructor property I would expect that the <| portion can be skipped, and the constructor would just be modified directly to point back at the enclosing object literal.

In to Allen's suggestion that the constructor property automatically point bag to the enclosing object literal, I think that might be ok, but I think its also brittle, because there are many other cases where that will likely not be the case such as

constuctor:function(){...}
//and
obj.constructor = function(){...}

ways of building the object. Therefore, leaving the wiring to the class operator, I think, is more robust.

Furthermore, knowing that class operator does a little more work, and will often create a new function, I would propose that it also allow:

class Point {
     ...
}

without assignment. This could be used to fix the constructor name as brought up by Brendan.

# Allen Wirfs-Brock (14 years ago)

On Nov 15, 2011, at 8:16 AM, Russell Leggett wrote:

...

As has been said, a missing constructor would not result in the expected behavior. I can also imagine other scenarios where the behavior would be unusual.

I agree, there is an issue with missing constructors. Certainly my example were assume that it would never be missing from objects would be used as object exemplars.

It's pretty clear that to solve this (if it is solvable) requires a more complex definition of the class operator. I'm thinking about that now. You examples are helpful...

# Irakli Gozalishvili (14 years ago)

On Monday, 2011-11-14 at 16:50 , David Flanagan wrote:

On 11/14/11 4:16 PM, Allen Wirfs-Brock wrote:

On Nov 14, 2011, at 3:15 PM, David Flanagan wrote:

I have a bad feeling about making 'new' work with both functions and object exemplars. If we can have two different types of classes, we're going to end up using typeof on our class objects to figure out what kind of class they are. If I've got a value C from a library and I think it is an object exemplar, but it is in fact a constructor function, then 'class C' is going to return Function rather than C itself...

Well, new'ing object exemplars has always been the central concept of our discussions about them. Essentially it is the self style of object creation and arguably the way the prototypal instantiation is supposed to work. It seems to be what people who really like prototypal inheritance really want to do.

Apparently I wasn't paying attention to the early discussions about object exemplars. I've heard the term used, but missed the point about changing the behavior of new. It seems to me people who want to use self-style object creation can use Object.create() and people who want to use JavaScript-style object creation can use new like we've been doing for 15 years. (Just today I wrote a blog post explaining why I'm hoping for classes in ES.next, and included, as part of my argument, the assertion that all the proposals on the table are just syntax sugar without new language semantics. I was wrong about that, I guess!)

The "magic" of what I'm proposing is that you will rarely have to worry about whether a named object abstraction (call it Foo) you get from a library is an object exemplar or a function/class exemplar. Without knowing you can say: new Foo(args)

Maybe I'm overreacting, and new semantics can make this all just work. But it seems like a major, major change to the language.

and it will work. Without knowing you can do either : let subfoo = Foo<| {constructor() {}}; or let subfoo = class Foo<| {constructor() {}} //note this parses as: class (Foo<| {... }) and things will work. You can even say: let subfoo = Foo<| function() {} and the right thinks happen.

To me, this seems like the essence of object-oreinted implementation encapsulation. All a client of Foo needs to know is that Foo is an "exemplar" and hence it can be instantiated (via new) or specialized (via<|). It is up to the implementor of Foo to decide how to best express its implementation. They can even change their mind and clients shouldn't care. Do JavaScript programmers want exemplars or do they want classes?

Some including me, want exemplars while others want classes. Good thing is that with this proposal both of us will get what we want :)

# Irakli Gozalishvili (14 years ago)

Sorry I'm so excited about this that I just could not read it all before posting this, my apologies if it's already discussed:

On Monday, 2011-11-14 at 17:04 , Allen Wirfs-Brock wrote:

UnaryExpression : class UnaryExpression ...

The semantics are:

  1. if UnaryExpression is undefined or null, return the value of UnaryExpression.
  2. Let obj be ToObject(UnaryExpression)
  3. Return the result of calling the [[Get]] internal method of obj with argument 'constructor'

Interesting, so 'constructor' will inherit from Object.prototype if missing from the exemplar. This means

let Point = class { x:0, y,0

};

let p = new Point(1, 2);

will result in p being constructed via the equivalent of new Number(1).

sh*t! I though I'd had a solution for this, but maybe I don't...

If you are really going to define either an object or class exemplar, you really need to define a constructor. The object exemplar case actually works out ok in this case because the inherited constructor is "called as a function", not "as a constructor". So for

let Point = { x: 0, y: 0 }; let p = new Point(1,2);

Is pretty much equivalent to: let p = Point <| {};

which may not be exactly what the programmer expected but it is a least in the vicinity and yields an object with (inherited) x and y properties.

I think this concern could be easily fixed if class prototype was equivalent of Class(prototype) where implementation of Class is following (BTW I don't propose to use Class function just illustration of what class prototype could be) :

function Class(prototype) { function constructor() { Object.hasOwnProperty(prototype, 'constructor') let instance = Object.create(prototype); instance.constructor.apply(instance, arguments); return instance; } constructor.prototype = prototype; return constructor; }

As additional benefit this solves issue of shared / frozen constructor properties of the prototype.

P.S.: I just yesterday I started experimenting with Class function in combination with selfish.

# Brendan Eich (14 years ago)

On Nov 15, 2011, at 8:16 AM, Russell Leggett wrote:

I think this example is contrived, but illustrates the point that JavaScript is very dynamic, and sometimes in the building process an object can be out of sync from its constructor. My expectations when I see this would be that my results would be a constructor function that can be used to produce a new version of the UnaryExpression, calling its constructor code. The best way I can think of to do this would be:

function classify(obj){
    let sub = obj <| {
        constructor(...args){
            super.constructor(...args);
        }
    }
    sub.constructor.prototype = sub;
    return sub.constructor;
}

The key is that the UnaryExpression is not modified, and it should still work even if a constructor is missing. If the UnaryExpression is unobservable and had an own constructor property I would expect that the <| portion can be skipped, and the constructor would just be modified directly to point back at the enclosing object literal.

Optimizations must be unobservable except by benchmarking or other kinds of profiling. But if I'm following you correctly, even if the UnaryExpression operand is a fresh object literal with an own 'constructor' property, the result of skipping <| differs observably from the case where the operand is not fresh:

class {p: "I'm own"}

vs.

class compute_object_with_own_p();

where compute_object_with_own_p returns an object {p: "I'm own too"}.

In the latter case, returning a new object whose [[Prototype]] references the non-fresh {p: "I'm own too"} object makes an observable difference in where 'p' lives.

# Brendan Eich (14 years ago)

On Nov 15, 2011, at 9:56 AM, Brendan Eich wrote:

On Nov 15, 2011, at 8:16 AM, Russell Leggett wrote:

I think this example is contrived, but illustrates the point that JavaScript is very dynamic, and sometimes in the building process an object can be out of sync from its constructor. My expectations when I see this would be that my results would be a constructor function that can be used to produce a new version of the UnaryExpression, calling its constructor code. The best way I can think of to do this would be:

function classify(obj){ let sub = obj <| { constructor(...args){ super.constructor(...args); } } sub.constructor.prototype = sub; return sub.constructor; }

The key is that the UnaryExpression is not modified, and it should still work even if a constructor is missing. If the UnaryExpression is unobservable and had an own constructor property I would expect that the <| portion can be skipped, and the constructor would just be modified directly to point back at the enclosing object literal.

Optimizations must be unobservable except by benchmarking or other kinds of profiling. But if I'm following you correctly, even if the UnaryExpression operand is a fresh object literal with an own 'constructor' property, the result of skipping <| differs observably from the case where the operand is not fresh:

class {p: "I'm own"}

Oops, that should have been:

class {p: "I'm own", constructor(){}}

of course.

# David Flanagan (14 years ago)

On 11/14/11 3:01 PM, Allen Wirfs-Brock wrote:

On Nov 14, 2011, at 2:01 PM, Brendan Eich wrote:

On Nov 14, 2011, at 12:16 PM, Allen Wirfs-Brock wrote:

let Point = { x:0, y,0, constructor(x,y} { this.x=x; this.y=y; } }.constructor; //<----------- note added property reference

let p = new Point(1,2); //new operator applied to a constructible function

So can you spec operator new a bit? It tries [[Construct]] and if missing, tries for a .constructor property that it calls on a fresh instance of Object whose [[Prototype]] is the exemplar?

Yup, that's essentially the semantics. I'm going to write up the object exemplar strawman which will cover this (and also instanceof).

So if the new operator will work correctly with both object and function exemplars, then what do we need the class operator for? Given an object exemplar C, will 'new C()' and 'new class C()' do exactly the same thing? That seems bad. Sorry if I'm being obtuse, but I can't see why we need both new semantics for 'new' and a 'class' operator.

# Russell Leggett (14 years ago)

On Tue, Nov 15, 2011 at 1:01 PM, Brendan Eich <brendan at mozilla.com> wrote:

On Nov 15, 2011, at 9:56 AM, Brendan Eich wrote:

On Nov 15, 2011, at 8:16 AM, Russell Leggett wrote:

I think this example is contrived, but illustrates the point that JavaScript is very dynamic, and sometimes in the building process an object can be out of sync from its constructor. My expectations when I see this would be that my results would be a constructor function that can be used to produce a new version of the UnaryExpression, calling its constructor code. The best way I can think of to do this would be:

function classify(obj){ let sub = obj <| { constructor(...args){ super.constructor(...args); } } sub.constructor.prototype = sub; return sub.constructor; }

The key is that the UnaryExpression is not modified, and it should still work even if a constructor is missing. If the UnaryExpression is unobservable and had an own constructor property I would expect that the <| portion can be skipped, and the constructor would just be modified directly to point back at the enclosing object literal.

Optimizations must be unobservable except by benchmarking or other kinds of profiling. But if I'm following you correctly, even if the UnaryExpression operand is a fresh object literal with an own 'constructor' property, the result of skipping <| differs observably from the case where the operand is not fresh:

class {p: "I'm own"}

Oops, that should have been:

class {p: "I'm own", constructor(){}}

of course.

/be

Yes, of course you're right about that. There was something in the back of my brain saying there might be a problem with that optimization, but I had hoped it could be done, because that is probably the most common case. I suppose you could say that it isn't an optimization, but simply part of the semantics. Not sure if that would be confusing or just seem like the right behavior.

# David Flanagan (14 years ago)

On 11/14/11 5:17 PM, Allen Wirfs-Brock wrote:

On Nov 14, 2011, at 4:50 PM, David Flanagan wrote:

On 11/14/11 4:16 PM, Allen Wirfs-Brock wrote:

On Nov 14, 2011, at 3:15 PM, David Flanagan wrote:

I have a bad feeling about making 'new' work with both functions and object exemplars. If we can have two different types of classes, we're going to end up using typeof on our class objects to figure out what kind of class they are. If I've got a value C from a library and I think it is an object exemplar, but it is in fact a constructor function, then 'class C' is going to return Function rather than C itself... Well, new'ing object exemplars has always been the central concept of our discussions about them. Essentially it is the self style of object creation and arguably the way the prototypal instantiation is supposed to work. It seems to be what people who really like prototypal inheritance really want to do. Apparently I wasn't paying attention to the early discussions about object exemplars. I've heard the term used, but missed the point about changing the behavior of new. It seems to me people who want to use self-style object creation can use Object.create() and people who want to use JavaScript-style object creation can use new like we've been doing for 15 years. (Just today I wrote a blog post explaining why I'm hoping for classes in ES.next, and included, as part of my argument, the assertion that all the proposals on the table are just syntax sugar without new language semantics. I was wrong about that, I guess!) Object create doesn't do the job because it doesn't provide for calling an "initialize" methods (turns out to be a very important part of the self-style).

Its not like there's a large community of self programmers out there who are migrating to JavaScript... It seems to me that if you like the object exemplar style, then you don't want to be using the new operator and constructor functions (or things that appear to be constructors because they're used with new). Instead you want to define factory functions that use Object.create():

function Range(from, to) { return Object.create(Range.methods, { from: { value: from, enumerable: true }, to: { value: from, enumerable: true} }); } Range.methods = { includes: function(x) { return this.from <= x && x <= this.to; } }; Range(1,3).includes(2); // => true

Do JavaScript programmers want exemplars or do they want classes? They seem to be split. Some vocally ask for better support for prototypal inheritance other vocally ask for classes.

Irakli has self-identified in this thread as wanting object exemplars but he has also defined a simple and useful Class() function for working with them. I suspect that part of the appeal of object exemplars is that it is easier to write support functions like Class() for working with them. That is: we can make the proponents of object exemplars happy with good library support. But for classes, we need language support...

I think what everybody is asking for is a better way to "created named object abstractions". The term exemplar is just one that I introduced into the discussion to make it easier to talk about the different kinds of entities you might apply such names to. In some language you name a "class declaration", in others you name a prototypal instance, in JS you have historically named a function. Exemplar was intended to be a generic for such "named object abstractions". An object exemplar is a self style prototype, a class exemplar is the sort of thing you find in languages llke Java. A function exemplar is a JS constructor,

Are there existing languages that support more than one kind of exemplar at the language level? It seems to me that JS is stuck with function exemplars, and we should work to make those better, not add complexity to the language by adding support for another style of exemplar.

# Irakli Gozalishvili (14 years ago)

On Tuesday, 2011-11-15 at 10:58 , David Flanagan wrote:

On 11/14/11 5:17 PM, Allen Wirfs-Brock wrote:

On Nov 14, 2011, at 4:50 PM, David Flanagan wrote:

On 11/14/11 4:16 PM, Allen Wirfs-Brock wrote:

On Nov 14, 2011, at 3:15 PM, David Flanagan wrote:

I have a bad feeling about making 'new' work with both functions and object exemplars. If we can have two different types of classes, we're going to end up using typeof on our class objects to figure out what kind of class they are. If I've got a value C from a library and I think it is an object exemplar, but it is in fact a constructor function, then 'class C' is going to return Function rather than C itself...

Well, new'ing object exemplars has always been the central concept of our discussions about them. Essentially it is the self style of object creation and arguably the way the prototypal instantiation is supposed to work. It seems to be what people who really like prototypal inheritance really want to do.

Apparently I wasn't paying attention to the early discussions about object exemplars. I've heard the term used, but missed the point about changing the behavior of new. It seems to me people who want to use self-style object creation can use Object.create() and people who want to use JavaScript-style object creation can use new like we've been doing for 15 years. (Just today I wrote a blog post explaining why I'm hoping for classes in ES.next, and included, as part of my argument, the assertion that all the proposals on the table are just syntax sugar without new language semantics. I was wrong about that, I guess!)

Object create doesn't do the job because it doesn't provide for calling an "initialize" methods (turns out to be a very important part of the self-style).

Its not like there's a large community of self programmers out there who are migrating to JavaScript... It seems to me that if you like the object exemplar style, then you don't want to be using the new operator and constructor functions (or things that appear to be constructors because they're used with new). Instead you want to define factory functions that use Object.create():

But making it possible to reuse libraries that are written either object or class exemplar supporters is a huge win to me!

# Brendan Eich (14 years ago)

On Nov 15, 2011, at 10:34 AM, Russell Leggett wrote:

Yes, of course you're right about that. There was something in the back of my brain saying there might be a problem with that optimization, but I had hoped it could be done, because that is probably the most common case. I suppose you could say that it isn't an optimization, but simply part of the semantics. Not sure if that would be confusing or just seem like the right behavior.

Semantics that sometimes make a new object with the given operand as its proto, other times return the operand unobservably mutated, are not gonna fly. That's bad for human and robot readers (i.e., implementations).

# Russell Leggett (14 years ago)

On Nov 15, 2011, at 5:45 PM, Brendan Eich <brendan at mozilla.com> wrote:

On Nov 15, 2011, at 10:34 AM, Russell Leggett wrote:

Yes, of course you're right about that. There was something in the back of my brain saying there might be a problem with that optimization, but I had hoped it could be done, because that is probably the most common case. I suppose you could say that it isn't an optimization, but simply part of the semantics. Not sure if that would be confusing or just seem like the right behavior.

Semantics that sometimes make a new object with the given operand as its proto, other times return the operand unobservably mutated, are not gonna fly. That's bad for human and robot readers (i.e., implementations).

Fair enough. It loses some of its elegance in the common case, but not to the point that it becomes inelegant in my mind. Certainly when you even have to take into account the difference between an object literal with and without a constructor, always creating a new object is best.

At this point, since it seems to me easy enough to implement as a function, is there a reason to go all the way to an operator. I think if you include the ability to have a declarative style with a name, then it pushes it over the top from a little sugar, to a comfortable familiar syntax.

class Point {...} //constructor has the correct name, Point is a declared variable //vs let Point = class {...}

# Brendan Eich (14 years ago)

On Nov 15, 2011, at 4:08 PM, Russell Leggett wrote:

At this point, since it seems to me easy enough to implement as a function, is there a reason to go all the way to an operator. I think if you include the ability to have a declarative style with a name, then it pushes it over the top from a little sugar, to a comfortable familiar syntax.

class Point {...} //constructor has the correct name, Point is a declared variable //vs let Point = class {...}

That's where I am if we are going to add class, and dherman's proposal looks pretty good to me:

strawman:minimal_classes

Class body is custom, uses best-of special forms (method definitions without comma separators, no data property on the prototype declarative form, other declarative forms look like declarations).

If we could have radical class-as-operator that composes without mutation, I would be overjoyed. We should keep working on it, I agree with MarkM.

The other way to go is to make

class E

(for any expression E) always desugar to a new object with a new constructor that calls E.constructor if present:

do { let %e = E; let %f = function /* NAME */ (...args) { return ('constructor' in %e) ? %e.constructor.apply(this, args) : void 0; }; let %o = %e <| { constructor: %f }; %f.prototype = %o; %f; }

The /* NAME */ bit could be imputed from new syntax if we want to support

class N E

or (in this case a special form that restricts E to be an object literal):

class N {...}

There would be no comma abatement, no static or private declarations, and data property initialisers might be in ... and problematic on the prototype if they have mutable values.

I'm not selling this yet, just exploring the class-as-unary-operator idea and avoiding the "maybe it's a new object, maybe's the operand" problem by always making a new object.

# Allen Wirfs-Brock (14 years ago)

OK, I have a fix for the missing constructor problem. See: strawman:class_operator#missing_constructors

# Jake Verbaten (14 years ago)

5 Let proto be the value of the [[Prototype]] internal property of obj. 6 Return the result of evaluating this algorithm using the value of proto as the value of UnaryExpression * * What is the point of calling class recursively on the [[Prototype]] if the object does not have constructor or [[ctor]] ?

Other then that, this solves the issue in an intuitive way.

# Allen Wirfs-Brock (14 years ago)

On Nov 17, 2011, at 9:47 AM, Jake Verbaten wrote:

5 Let proto be the value of the [[Prototype]] internal property of obj. 6 Return the result of evaluating this algorithm using the value of proto as the value of UnaryExpression

What is the point of calling class recursively on the [[Prototype]] if the object does not have constructor or [[ctor]] ?

let Point = class {x:0,y:0}; let instance = new Point; if ((class instance) is Point) ...

# Russell Leggett (14 years ago)

On Thu, Nov 17, 2011 at 12:04 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

OK, I have a fix for the missing constructor problem. See: strawman:class_operator#missing_constructors

The biggest thing I see missing is a declarative form that includes the name. I just think its so close, that to not include a form

class Point {...}

Which can desugar to create a var Point at the very least, would be a bit of a tragedy. I think most people will see the class operator and think about it as analogous to function.

Another piece which might not be as big a deal is that [[ctor]] is not the same as 'constructor', and in the cases with a missing 'constructor', the result of 'class Proto' would seem like it makes (or fetches) a function that behaves like a proper constructor function, but it will result in an instance whose prototype does not have an own 'constructor' property. (I think I even confused myself with that, sorry)

The alternative proposal I've been advocating that always creates a new function and prototype makes tradeoffs in the other direction. It does not require [[ctor]], or automatic .prototype references from constructor to the enclosing object literal. It also makes it trivial to include a name on the constructor function without mutation (because a new function is being made). OTOH, because it always creates a new function with a fresh prototype that points up to UnaryExpression, you get this important difference:

let f = new Foo();
let isSame = (class f === Foo);
//isSame is true for allen's proposal, but false for mine.
//however, mine would behaviorally be the same (barring serious

inspection)

At this point, if Allen's proposal included a declaration form 'class Point {...}' I would be ready to get behind it. I still have a fondness for mine, which is used purely for defining classes, but for most cases, his would work equally well in the ways I would want to use it.

# Axel Rauschmayer (14 years ago)

I agree with the advantage of a declaration containing a name and with it being similar to functions.

I tried a synthesis of the current inheritance/class proposals: gist.github.com/1374226

Comments welcome; it might please nobody by trying to please everybody.

# Brendan Eich (14 years ago)

On Nov 17, 2011, at 9:04 AM, Allen Wirfs-Brock wrote:

OK, I have a fix for the missing constructor problem. See: strawman:class_operator#missing_constructors

Nit: [[ctor]], obviously a temporary hack-name. How about [[ClassConstructor]]? or maybe [[DefaultConstructor]]?

Non-nit: don't we want inherited 'constructor' to work, unless it has the value Object?

# Jake Verbaten (14 years ago)

On Thu, Nov 17, 2011 at 10:05 PM, Brendan Eich <brendan at mozilla.com> wrote:

On Nov 17, 2011, at 9:04 AM, Allen Wirfs-Brock wrote:

OK, I have a fix for the missing constructor problem. See: strawman:class_operator#missing_constructors

Nit: [[ctor]], obviously a temporary hack-name. How about [[ClassConstructor]]? or maybe [[DefaultConstructor]]?

Non-nit: don't we want inherited 'constructor' to work, unless it has the value Object?

For a chain Object <| Parent <| Child. We wouldn't want class Child to return Parent.constructor because then we would be creating new instances of Parent whenever we invoke new Child.

We may want [[ctor]] that's generated on Child to invoke Parent.constructor as a default action rather then just be an empty function.

This would involve changing

The value of this internal property is a new function object defined as

if by function(){}.

To

The value of this internal property is a new function object defined as if by function(...args){ super.constructor(...args); }.

Personally I think invoking the super constructor as default behaviour makes more sense. However we may want to optimise this so it only invokes the super constructor if the super constructor is not Object.

[ credit to Axel ( gist.github.com/1374226 ) for default constructors invoking super.constructor ]

# Brendan Eich (14 years ago)

On Nov 17, 2011, at 2:47 PM, Jake Verbaten wrote:

This would involve changing

The value of this internal property is a new function object defined as if by function(){}.

To

The value of this internal property is a new function object defined as if by function(...args){ super.constructor(...args); }.

Personally I think invoking the super constructor as default behaviour makes more sense. However we may want to optimise this so it only invokes the super constructor if the super constructor is not Object.

[ credit to Axel ( gist.github.com/1374226 ) for default constructors invoking super.constructor ]

That's it, thanks.

# Allen Wirfs-Brock (14 years ago)

On Nov 17, 2011, at 2:05 PM, Brendan Eich wrote:

On Nov 17, 2011, at 9:04 AM, Allen Wirfs-Brock wrote:

OK, I have a fix for the missing constructor problem. See: strawman:class_operator#missing_constructors

Nit: [[ctor]], obviously a temporary hack-name. How about [[ClassConstructor]]? or maybe [[DefaultConstructor]]?

:-)

Non-nit: don't we want inherited 'constructor' to work, unless it has the value Object?

let BasicPoint = class {x:0, y:0, constructor() {}}; let BetterPoint = class BasicPoint <| {better: true}; let bp = new BetterPoint; print(bp.better ? "wrong: bp [[Prototype]] is BasicPoint. prototype" : "correct: bp [[Prototype]] is BetterPoint.prototype");

BetterPoint needs a [[DefaultConstructor]] for the same reason that the original Point example does.

We can debate whether Default Constructors should do a: if (super.constructor isnt Object) super.constructor() but that is a more basic question about whether constructors (default or otherwise) should always do implicit super calls.

# Brendan Eich (14 years ago)

On Nov 17, 2011, at 3:31 PM, Allen Wirfs-Brock wrote:

We can debate whether Default Constructors should do a: if (super.constructor isnt Object) super.constructor() but that is a more basic question about whether constructors (default or otherwise) should always do implicit super calls.

We should debate. What do you think?

# Jake Verbaten (14 years ago)

On Thu, Nov 17, 2011 at 11:39 PM, Brendan Eich <brendan at mozilla.com> wrote:

On Nov 17, 2011, at 3:31 PM, Allen Wirfs-Brock wrote:

We can debate whether Default Constructors should do a: if (super.constructor isnt Object) super.constructor() but that is a more basic question about whether constructors (default or otherwise) should always do implicit super calls.

We should debate. What do you think?

Personally I cannot think of any examples where this functionality would be unwanted.

This debate will probably depend on the counter examples where invoking the super constructor implicitly is unwanted behavior.

An argument for doing this is that you can use initialize or some other name for constructor like functions that don't implicitly make super calls.

# Allen Wirfs-Brock (14 years ago)

On Nov 17, 2011, at 3:47 PM, Jake Verbaten wrote:

On Thu, Nov 17, 2011 at 11:39 PM, Brendan Eich <brendan at mozilla.com> wrote: On Nov 17, 2011, at 3:31 PM, Allen Wirfs-Brock wrote:

We can debate whether Default Constructors should do a: if (super.constructor isnt Object) super.constructor() but that is a more basic question about whether constructors (default or otherwise) should always do implicit super calls.

We should debate. What do you think?

Personally I cannot think of any examples where this functionality would be unwanted.

This debate will probably depend on the counter examples where invoking the super constructor implicitly is unwanted behavior.

An argument for doing this is that you can use initialize or some other name for constructor like functions that don't implicitly make super calls.

In general, you don't want a constructor to automatically do super.constructor(...args) because a "sub-exemplar" may use a different argument signature from its "super-exemplar".

For this reason, my first reaction was: If we don't (and we shouldn't) do an implicit super.constructor(...args) call for things like: {constructor() {} } then we also should not do it for the [[DefaultConstructor]] created for { }. However, I think I'm wavering. The programmer hasn't actually written any code so there isn't a "different argument signature" to worry about. so, may it would be ok to do a not to Object super.constructor call.

# Allen Wirfs-Brock (14 years ago)

On Nov 17, 2011, at 11:21 AM, Russell Leggett wrote:

sorry that last one didn't go to the group - ignore. This new gmail interface screwed me up ;)

On Thu, Nov 17, 2011 at 2:20 PM, Russell Leggett <russell.leggett at gmail.com> wrote:

At this point, if Allen's proposal included a declaration form 'class Point {...}' I would be ready to get behind it. I still have a fondness for mine, which is used purely for defining classes, but for most cases, his would work equally well in the ways I would want to use it.

Here a first (for me for this form) cut:

class AbstractPoint { }

and

class Point AbstractPoiont <| { }

where the syntax of the second is:

classPoint Identifier MemberExpression <| LiteralObject

This means exactly: let Identifier = class MemberExpression <| LiteralObject

(may need some grammar engineering because MemberExpression can contain a <|)

The expression form remains exactly as I've already defined in it strawman:class_operator

Strictly speaking the <| is unnecessary and could be implicit. I like requiring it because it maintains symmetry with the expression form.

For the relatively rare situations where "class-side" methods are needed they could be provided as part of the ObjectLiteral, as:

class Point AbstractPoiont <| { constructor() {}. {origin() {return new this(0,0)}} //class methods are just properties on the constructor function

# Allen Wirfs-Brock (14 years ago)

I updated the strawman with a declaration form of the class operator: strawman:class_operator#a_binding_declaration_form

the strawman is somewhat different from what I described below.