Finding a "safety syntax" for classes

# Russell Leggett (9 years ago)

The recent discussion “Using Object Literals as Classes” brought up some key points, but the general feeling I got and agree with is this: we need to get classes into ES6. As Kevin points out, even if it didn’t have all of the features (like mixins) that most class libs have, he would use an extremely minimal class syntax. I think CoffeeScript is proof that others feel the same way. CoffeeScript classes are just a wrapper around the “standard” way of making classes and is completely interoperable with vanilla JS constructor function “classes”. It does not really have bells and whistles - just a nice declarative syntax, including support for extension and super.

The subject of classes has come up countless times and still we have no resolution. Brendan has phrased it as the “goldilocks proposal” - it can’t be too big or too small, but needs to be “just right”. I would suggest what we need right now is a different analogy. What we need is the “safety syntax”. For those unfamiliar with the idea, in the states we have a term “safety school” - when applying for colleges, it is recommended to apply to at least one college that you would be satisfied going to and can be almost guaranteed to get into. This way, worst case scenario, if you don’t get into all the other schools you like better, at least you can fall back on your “safety school”.

Is it possible that we can come up with a class syntax that we can all agree is better than nothing, and importantly, leaves possibilities open for future enhancement? As a “safety syntax” this doesn’t mean we stop trying to find a better syntax, it just means that if we don’t find it then we still have something – something that we can make better in ES7.

I would propose that the absolute minimal requirements would be:

  • has a declaration form that uses the class keyword and an identifier to create the class
  • has a body that can include both the constructor function, as well as any instance (prototype) methods – including getter and setter properties
  • can declare the class as a subclass of a another class (probably with the extends keyword)
  • super is available from any of the methods or constructor function

The following is an example from the CoffeeScript website, and just converted to what seemed like a logical JS version:

class Animal {

constructor(name){

    this.name = name;

}

move(meters){

    alert(this.name + " moved " + meters + "m.");

}

}

class Snake extends Animal {

constructor(name){

    super(name);

}

move(){

    alert("Slithering...");

    super.move(5);

}

}

class Horse extends Animal {

constructor(name){

    super(name);

}

move(){

    alert("Galloping...");

    super.move(45);

}

}

let sam = new Snake("Sammy the Python");

let tom = new Horse("Tommy the Palomino");

A few things that are different from CoffeeScript:

  • if there is no constructor, it doesn’t automatically pass arguments up to the parent constructor. I think that would be nice but more controversial.
  • to call super on a method you have to indicate the method super.move
  • while not obvious in the example, I would say the class body should
    • be its own construct, not just an object literal - this makes it easier to have methods without ,’s and be future-proof for whatever we might want later
    • only allow a constructor function and methods - nothing else. No public/private fields. No worrying about var x = {a:b} inside the class body – if it should be allowed, what the syntax should be etc. With the "safety syntax", less is more as long as it gets accepted.

So what do you say people? Is it safe enough? One of the biggest arguments I’ve heard against rushing in a class syntax now is that once its in we have to keep supporting it. I say that this is small enough we won’t regret it, and makes it possible to do a lot more in the future. If something more substantial can be agreed on soon enough to make it into ES6, even better, but maybe we can at least have a backup plan.

# Rick Waldron (9 years ago)

On Mon, Mar 19, 2012 at 4:03 PM, Russell Leggett <russell.leggett at gmail.com>wrote:

The recent discussion “Using Object Literals as Classes” brought up some key points, but the general feeling I got and agree with is this: we need to get classes into ES6. As Kevin points out, even if it didn’t have all of the features (like mixins) that most class libs have, he would use an extremely minimal class syntax. I think CoffeeScript is proof that others feel the same way. CoffeeScript classes are just a wrapper around the “standard” way of making classes and is completely interoperable with vanilla JS constructor function “classes”. It does not really have bells and whistles - just a nice declarative syntax, including support for extension and super.

The subject of classes has come up countless times and still we have no resolution. Brendan has phrased it as the “goldilocks proposal” - it can’t be too big or too small, but needs to be “just right”. I would suggest what we need right now is a different analogy. What we need is the “safety syntax”. For those unfamiliar with the idea, in the states we have a term “safety school” - when applying for colleges, it is recommended to apply to at least one college that you would be satisfied going to and can be almost guaranteed to get into. This way, worst case scenario, if you don’t get into all the other schools you like better, at least you can fall back on your “safety school”.

Is it possible that we can come up with a class syntax that we can all agree is better than nothing, and importantly, leaves possibilities open for future enhancement? As a “safety syntax” this doesn’t mean we stop trying to find a better syntax, it just means that if we don’t find it then we still have something – something that we can make better in ES7.

I would propose that the absolute minimal requirements would be:

  • has a declaration form that uses the class keyword and an identifier to create the class
  • has a body that can include both the constructor function, as well as any instance (prototype) methods – including getter and setter properties
  • can declare the class as a subclass of a another class (probably with the extends keyword)
  • super is available from any of the methods or constructor function

The following is an example from the CoffeeScript website, and just converted to what seemed like a logical JS version:

class Animal {

constructor(name){

    this.name = name;

}

move(meters){

    alert(this.name + " moved " + meters + "m.");

}

}

class Snake extends Animal {

constructor(name){

    super(name);

}

move(){

    alert("Slithering...");

    super.move(5);

}

}

class Horse extends Animal {

constructor(name){

    super(name);

}

move(){

    alert("Galloping...");

    super.move(45);

}

}

let sam = new Snake("Sammy the Python");

let tom = new Horse("Tommy the Palomino");

Does |move| become an instance property method or a prototype property method?

# Rick Waldron (9 years ago)

On Tue, Mar 20, 2012 at 1:08 PM, Rick Waldron <waldron.rick at gmail.com>wrote:

On Mon, Mar 19, 2012 at 4:03 PM, Russell Leggett < russell.leggett at gmail.com> wrote:

The recent discussion “Using Object Literals as Classes” brought up some key points, but the general feeling I got and agree with is this: we need to get classes into ES6. As Kevin points out, even if it didn’t have all of the features (like mixins) that most class libs have, he would use an extremely minimal class syntax. I think CoffeeScript is proof that others feel the same way. CoffeeScript classes are just a wrapper around the “standard” way of making classes and is completely interoperable with vanilla JS constructor function “classes”. It does not really have bells and whistles - just a nice declarative syntax, including support for extension and super.

The subject of classes has come up countless times and still we have no resolution. Brendan has phrased it as the “goldilocks proposal” - it can’t be too big or too small, but needs to be “just right”. I would suggest what we need right now is a different analogy. What we need is the “safety syntax”. For those unfamiliar with the idea, in the states we have a term “safety school” - when applying for colleges, it is recommended to apply to at least one college that you would be satisfied going to and can be almost guaranteed to get into. This way, worst case scenario, if you don’t get into all the other schools you like better, at least you can fall back on your “safety school”.

Is it possible that we can come up with a class syntax that we can all agree is better than nothing, and importantly, leaves possibilities open for future enhancement? As a “safety syntax” this doesn’t mean we stop trying to find a better syntax, it just means that if we don’t find it then we still have something – something that we can make better in ES7.

I would propose that the absolute minimal requirements would be:

  • has a declaration form that uses the class keyword and an identifier to create the class
  • has a body that can include both the constructor function, as well as any instance (prototype) methods – including getter and setter properties
  • can declare the class as a subclass of a another class (probably with the extends keyword)
  • super is available from any of the methods or constructor function

The following is an example from the CoffeeScript website, and just converted to what seemed like a logical JS version:

class Animal {

constructor(name){

    this.name = name;

Nevermind my previous message - somehow my eyes skimmed right past this.

Sorry for the noise

# Russell Leggett (9 years ago)

I would propose that the absolute minimal requirements would be:

  • has a declaration form that uses the class keyword and an identifier to create the class
  • has a body that can include both the constructor function, as well as any instance (prototype) methods – including getter and setter properties
  • can declare the class as a subclass of a another class (probably with the extends keyword)
  • super is available from any of the methods or constructor function

Does |move| become an instance property method or a prototype property method?

Yes - I apologize for any confusion - move would be a method on the class' prototype. I'm not really suggesting anything new, just a simple syntactic sugar over the normal way of creating a constructor function + prototype.

# Bob Nystrom (9 years ago)

On Mon, Mar 19, 2012 at 1:03 PM, Russell Leggett <russell.leggett at gmail.com>wrote:

The recent discussion “Using Object Literals as Classes” brought up some key points, but the general feeling I got and agree with is this: we need to get classes into ES6. As Kevin points out, even if it didn’t have all of the features (like mixins) that most class libs have, he would use an extremely minimal class syntax. I think CoffeeScript is proof that others feel the same way. CoffeeScript classes are just a wrapper around the “standard” way of making classes and is completely interoperable with vanilla JS constructor function “classes”. It does not really have bells and whistles - just a nice declarative syntax, including support for extension and super.

The subject of classes has come up countless times and still we have no resolution. Brendan has phrased it as the “goldilocks proposal” - it can’t be too big or too small, but needs to be “just right”. I would suggest what we need right now is a different analogy. What we need is the “safety syntax”. For those unfamiliar with the idea, in the states we have a term “safety school” - when applying for colleges, it is recommended to apply to at least one college that you would be satisfied going to and can be almost guaranteed to get into. This way, worst case scenario, if you don’t get into all the other schools you like better, at least you can fall back on your “safety school”.

Is it possible that we can come up with a class syntax that we can all agree is better than nothing, and importantly, leaves possibilities open for future enhancement? As a “safety syntax” this doesn’t mean we stop trying to find a better syntax, it just means that if we don’t find it then we still have something – something that we can make better in ES7.

I would propose that the absolute minimal requirements would be:

  • has a declaration form that uses the class keyword and an identifier to create the class
  • has a body that can include both the constructor function, as well as any instance (prototype) methods – including getter and setter properties
  • can declare the class as a subclass of a another class (probably with the extends keyword)
  • super is available from any of the methods or constructor function

The following is an example from the CoffeeScript website, and just converted to what seemed like a logical JS version:

class Animal {

constructor(name){

    this.name = name;

}

move(meters){

    alert(this.name + " moved " + meters + "m.");

}

}

class Snake extends Animal {

constructor(name){

    super(name);

}

move(){

    alert("Slithering...");

    super.move(5);

}

}

class Horse extends Animal {

constructor(name){

    super(name);

}

move(){

    alert("Galloping...");

    super.move(45);

}

}

let sam = new Snake("Sammy the Python");

let tom = new Horse("Tommy the Palomino");

A few things that are different from CoffeeScript:

  • if there is no constructor, it doesn’t automatically pass arguments up to the parent constructor. I think that would be nice but more controversial.
  • to call super on a method you have to indicate the method super.move
  • while not obvious in the example, I would say the class body should
    • be its own construct, not just an object literal - this makes it easier to have methods without ,’s and be future-proof for whatever we might want later
    • only allow a constructor function and methods - nothing else. No public/private fields. No worrying about var x = {a:b} inside the class body – if it should be allowed, what the syntax should be etc. With the "safety syntax", less is more as long as it gets accepted.

So what do you say people? Is it safe enough?

I love it. I would love it if we could be in the mode of "now we have a class foundation, are there things we want to build on" and less in the mode of "can I come to grips with the idea of having classes in JS at all?" I like your proposal, and the analogy.

# Rick Waldron (9 years ago)

On Tue, Mar 20, 2012 at 1:21 PM, Russell Leggett <russell.leggett at gmail.com>wrote:

I would propose that the absolute minimal requirements would be:

  • has a declaration form that uses the class keyword and an identifier to create the class
  • has a body that can include both the constructor function, as well as any instance (prototype) methods – including getter and setter properties
  • can declare the class as a subclass of a another class (probably with the extends keyword)
  • super is available from any of the methods or constructor function

Does |move| become an instance property method or a prototype property method?

Yes - I apologize for any confusion - move would be a method on the class' prototype. I'm not really suggesting anything new, just a simple syntactic sugar over the normal way of creating a constructor function + prototype.

Got it - I had missed this.name (which implies that instance stuff is created inside constructor()... ), but thank you for clarifying

# Rick Waldron (9 years ago)

[ ... snip ]

class Animal {

constructor(name){

    this.name = name;

}

move(meters){

    alert(this.name + " moved " + meters + "m.");

}

}

Russ,

I'm trying to write up some supporting examples, but ran into a snag regarding "static" properties. eg

function Foo( stuff ) { this.stuff = stuff || ""; }

Foo.prototype.getStuff = function() { return this.stuff; };

Foo.Bar = function() { return new Foo("bar"); };

How would I make Foo.Bar ?

# Herby Vojčík (9 years ago)

Russell Leggett wrote:

Is it possible that we can come up with a class syntax that we can all agree is better than nothing, and importantly, leaves possibilities open for future enhancement? As a “safety syntax” this doesn’t mean we stop trying to find a better syntax, it just means that if we don’t find it then we still have something – something that we can make better in ES7.

I would propose that the absolute minimal requirements would be:

* has a declaration form that uses the class keyword and an
  identifier to create the class

Why to rule out other possibilities (though I understand this one is the "least surprise" one)?

* has a body that can include both the constructor function, as well
  as any instance (prototype) methods – including getter and setter
  properties
* can declare the class as a subclass of a another class (probably
  with the extends keyword)
* super is available from any of the methods or constructor function

Ok.

The following is an example from the CoffeeScript website, and just converted to what seemed like a logical JS version:

class Animal { constructor(name){ this.name = name; }

 move(meters){
     alert(this.name + " moved " + meters + "m.");
 }

}

class Snake extends Animal { constructor(name){ super(name); }

 move(){
     alert("Slithering...");
     super.move(5);
 }

}

class Horse extends Animal { constructor(name){ super(name); }

 move(){
     alert("Galloping...");
     super.move(45);
 }

}

let sam = new Snake("Sammy the Python");

let tom = new Horse("Tommy the Palomino");

A few things that are different from CoffeeScript:

* if there is no constructor, it doesn’t automatically pass
  arguments up to the parent constructor. I think that would be nice
  but more controversial.

A thing for discussion. Clear is clear, but you define the constructor anyway (you have to, it's the JS way). Maybe there should be extends* to say 'auto-generate forwarding, not clear one'. Though this is not the part of "safety" plan.

* to call super on a method you have to indicate the method super.move
* while not obvious in the example, I would say the class body should
      o be its own construct, not just an object literal - this

-1. See more below.

        makes it easier to have methods without ,’s and be
        future-proof for whatever we might want later

I have a lot of reasons (gut instinct, too, but also some though experiments on how things get powerful and concise things can be if classes automaticall had all the possibilities of (potientially revved-up) object literals [1]). So I need to see a really strong argument for abandoning the cause of "body of class should be the same as the body of object literal (with possible little numbers of changes)".

So my -1 holds if you want to add anything that goes beyond the object-literal syntax, and I'd give -0.66 in case where you would restrict it too much.

If you restrict it to only hold constructor and methods, it is still a compatible (though stripped) object-literal - you can have methods there (constructor is nothing special, just a method named 'constructor'; the magic of class keyword grants it [[Construct]] and .prototype created from {} block and according to object literal syntax you can have methods there without comma), so this would be -0.66.

Without this restriction it would more or less be the same as proposal "3." in the thread unhappily named 'u' (my mail client probably did something wrong):

  1. 'class Name {...} [static {...}]' as a quasi-sugar for 'Name.prototype .{...} [.constructor.{...}] with Name body derived from constructor method in first {...}; always returning the constructor': (liberal use of <| for class declarations as well)

private arr;

class List { constuctor (n) { this. at arr = n === +n ? new Array(n) : []; } at (i) { i = +i; if (i>=0 && i<this. at arr.length) { return this. at arr[i]; } else throw "Out of bounds: "+i; } size () { return this. at arr.length; } } static { from (array) { var r = new this(); r. at arr = array; return r; } };

private writeArr, readOffset;

List <| class Queue { // no problem if here is 'extends', but // I'd like it for function declaration, too, then // as in thread "extends with functions" constructor () { super(); this. at writeArr = []; this. at readOffset = 0; } at (i) { /* needs redefinition */ } size () { return super.size() - this. at readOffset + this. at writeArr.length; } push (elements) { return writeArr.push.apply(writeArr, elements); } shift () { //reads from arr; shifts []->writeArr->arr if empty } }

// Queue inherits from, needs no static if it does not want something additional

I silently assume here that extends creates, of course, double chain (constuctor inherits as well as prototype inherits, not just the latter)

      o only allow a constructor function and methods - nothing
        else. No public/private fields. No worrying about var x =

private can be done as above - out of class - it does not need as part of its syntax

        {a:b} inside the class body – if it should be allowed, what
        the syntax should be etc. With the "safety syntax", less is
        more as long as it gets accepted.

So what do you say people? Is it safe enough? One of the biggest arguments I’ve heard against rushing in a class syntax now is that once its in we have to keep supporting it. I say that this is small enough we won’t regret it, and makes it possible to do a lot more in the future.

It's too small. Whenever I want a shared field in prototype (other than method), I still must

MyClass.prototype.{ sharedBar: 0, sharedBaz: "" };

This makes its inclusion a bit questionable (same for statics).

Yes, we can go in little steps... but lots of them we already have (super expressions are already planned to be orthogonal to classes), and I think the next small step should be 'extends' for function declarations (which, unless <|, explicitly says "I am creating double chain here, in the spirit of 'classical' class inheritance; and if thing after extends is not constructor, I throw").

If something more substantial can be agreed on soon enough to make it into ES6, even better, but maybe we can at least have a backup plan.

  • Russ

Herby

[1] blog.herby.sk/blosxom/Programming/ES-next/empowered-data.html This adds functional data-definition power to object literals and as a consequence, trait based composition is in the classes for free

# Russell Leggett (9 years ago)

On Tue, Mar 20, 2012 at 2:09 PM, Rick Waldron <waldron.rick at gmail.com>wrote:

[ ... snip ]

class Animal {

constructor(name){

    this.name = name;

}

move(meters){

    alert(this.name + " moved " + meters + "m.");

}

}

Russ,

I'm trying to write up some supporting examples, but ran into a snag regarding "static" properties. eg

function Foo( stuff ) { this.stuff = stuff || ""; }

Foo.prototype.getStuff = function() { return this.stuff; };

Foo.Bar = function() { return new Foo("bar"); };

How would I make Foo.Bar ?

In my opinion, because it is a point of contention, there should be no special support for statics. It would be written exactly as you have it above, or you could do

Foo.{
    Bar() {
        return new Foo("bar");
    }
    //potentially more here
};

I will reiterate the goal here of a safety syntax. Right now, my goal is to just get some agreement on a class syntax where the future options are open in case we can't find something better. I would still be happy to use my minimal proposed class syntax because it gets past the hard part and nails the 80% of code that's annoying. Some support for statics could be added later if agreed upon but I'd hate to see it stop classes from happening.

# Rick Waldron (9 years ago)

In my opinion, because it is a point of contention, there should be no special support for statics. It would be written exactly as you have it above, or you could do

Sounds good to me!

I've taken my previous tri-lambda-syntax support example ( gist.github.com/2013909) and I've updated the code to use your proposed class syntax... It looks nice:

gist.github.com/2139330

# Russell Leggett (9 years ago)
  • has a declaration form that uses the class keyword and an

    identifier to create the class

Why to rule out other possibilities (though I understand this one is the "least surprise" one)?

I'm not sure I understand what you are asking, but I think you answered your own question. Remember - safety syntax. Minimal syntax without limiting future possibilities. We know that we will need "class Foo {". Let's just accept that as the base with future options later. And by later I mean "it can still be changed for ES6, just not for this discussion".

A few things that are different from CoffeeScript:

  • if there is no constructor, it doesn’t automatically pass

    arguments up to the parent constructor. I think that would be nice but more controversial.

A thing for discussion. Clear is clear, but you define the constructor anyway (you have to, it's the JS way). Maybe there should be extends* to say 'auto-generate forwarding, not clear one'. Though this is not the part of "safety" plan.

We would have to decide what happens if they don't supply a constructor. I think we would need a default, and in my mind there are 3 possibilities:

  1. Empty constructor - does nothing.
  2. Calls super with no args
  3. Calls super as a pass through

I'm fond of 3, but might be missing some controversial reason for one of the others. Personally, I would be willing to take any of them for the sake of classes.

* to call super on a method you have to indicate the method super.move
  • while not obvious in the example, I would say the class body should o be its own construct, not just an object literal - this

-1. See more below.

        makes it easier to have methods without ,’s and be
       future-proof for whatever we might want later

I have a lot of reasons (gut instinct, too, but also some though experiments on how things get powerful and concise things can be if classes automaticall had all the possibilities of (potientially revved-up) object literals [1]). So I need to see a really strong argument for abandoning the cause of "body of class should be the same as the body of object literal (with possible little numbers of changes)".

Ok, lets put it this way - my safety syntax only allows constructor + methods, with no commas in between. If commas after methods can be elided in object literal extensions then we're still on solid ground, it could go either way. What I would argue, though, is that we don't define the class body as equivalent to an object literal, there is still too much controversy there.

So my -1 holds if you want to add anything that goes beyond the object-literal syntax, and I'd give -0.66 in case where you would restrict it too much.

If you restrict it to only hold constructor and methods, it is still a compatible (though stripped) object-literal - you can have methods there (constructor is nothing special, just a method named 'constructor'; the magic of class keyword grants it [[Construct]] and .prototype created from {} block and according to object literal syntax you can have methods there without comma), so this would be -0.66.

Well then I guess its a -0.66 from you, but remember this is the safety syntax! The battle can rage on later.

Without this restriction it would more or less be the same as proposal "3." in the thread unhappily named 'u' (my mail client probably did something wrong):

  1. 'class Name {...} [static {...}]' as a quasi-sugar for 'Name.prototype .{...} [.constructor.{...}] with Name body derived from constructor method in first {...}; always returning the constructor': (liberal use of <| for class declarations as well)

private arr;

class List { constuctor (n) { this. at arr = n === +n ? new Array(n) : []; } at (i) { i = +i; if (i>=0 && i<this. at arr.length) { return this. at arr[i]; } else throw "Out of bounds: "+i; } size () { return this. at arr.length; } } static { from (array) { var r = new this(); r. at arr = array; return r; } };

private writeArr, readOffset;

List <| class Queue { // no problem if here is 'extends', but // I'd like it for function declaration, too, then // as in thread "extends with functions" constructor () { super(); this. at writeArr = []; this. at readOffset = 0; } at (i) { /* needs redefinition */ } size () { return super.size() - this. at readOffset + this. at writeArr.length; } push (elements) { return writeArr.push.apply(writeArr, elements); } shift () { //reads from arr; shifts []->writeArr->arr if empty } }

// Queue inherits from, needs no static if it does not want something additional

I silently assume here that extends creates, of course, double chain (constuctor inherits as well as prototype inherits, not just the latter)

Correct

      o only allow a constructor function and methods - nothing
       else. No public/private fields. No worrying about var x =

private can be done as above - out of class - it does not need as part of its syntax

Absolutely

        {a:b} inside the class body – if it should be allowed, what
       the syntax should be etc. With the "safety syntax", less is
       more as long as it gets accepted.

So what do you say people? Is it safe enough? One of the biggest arguments I’ve heard against rushing in a class syntax now is that once its in we have to keep supporting it. I say that this is small enough we won’t regret it, and makes it possible to do a lot more in the future.

It's too small. Whenever I want a shared field in prototype (other than method), I still must

MyClass.prototype.{ sharedBar: 0, sharedBaz: "" };

This makes its inclusion a bit questionable (same for statics).

If the alternative is no class syntax, you will have to do this anyway. Isn't this proposal still better than nothing?

Yes, we can go in little steps... but lots of them we already have (super expressions are already planned to be orthogonal to classes), and I think the next small step should be 'extends' for function declarations (which, unless <|, explicitly says "I am creating double chain here, in the spirit of 'classical' class inheritance; and if thing after extends is not constructor, I throw").

What would you rather have?

function Snake(name) extends Animal {
    super(name);
}.prototype.{
    move(){
        alert("Slithering...");
        super.move(5);
    }
}

or

class Snake extends Animal {
    constructor(name){
        super(name);
    }

    move(){
        alert("Slithering...");
        super.move(5);
    }
}

I know which one I would pick. At least as a safety syntax. The arguing can continue after that. I hope we get more than this minimum, but please, don't the indecision make the choice be "nothing".

# Russell Leggett (9 years ago)

On Tue, Mar 20, 2012 at 2:40 PM, Rick Waldron <waldron.rick at gmail.com>wrote:

In my opinion, because it is a point of contention, there should be no

special support for statics. It would be written exactly as you have it above, or you could do

Sounds good to me!

I've taken my previous tri-lambda-syntax support example ( gist.github.com/2013909) and I've updated the code to use your proposed class syntax... It looks nice:

gist.github.com/2139330

I agree, I think this looks great. Even the statics look pretty clear to me.

# David Herman (9 years ago)

I can mostly dig it -- I've been trying to encourage minimal classes for quite some time now.

The part that I'm least happy about is that this doesn't allow for declarative private methods, because if we had the computed syntax like what we're adding to object literals, it would clash with hoisting:

{
    let priv = new Name();
    class C {
        // oops, priv is still undefined!!
        [priv](x, y, z) {
            return x * y / z
        }
    }
}

But I suppose the answer to that would eventually be better declarative syntax for private, as we've discussed but deferred for post-ES6 consideration.

I do think that class declarations should be hoisted, both in the sense of being in scope for the whole containing block (as with let and function) and in the sense of being initialized before the block begins executing (as with function).

My only syntactic quibble: constructor is so inconveniently long. I've argued in the past for new as special syntax in class bodies that indicates a constructor.

  • short & sweet
  • consistent with get/set shorthand
  • already a keyword so it can be given special treatment
  • doesn't have to actually correspond to a property called "new", so no extra magic properties on top of .constructor
  • can still create a .constructor property as well
# Kevin Smith (9 years ago)

I do think that class declarations should be hoisted, both in the sense of being in scope for the whole containing block (as with let and function) and in the sense of being initialized before the block begins executing (as with function).

I've been on the fence on the hoisting issue. On the one hand, they're "declarations", and sugar for functions, so maybe they should be hoisted. If you want to have statics though (which I realize is being put aside for this discussion), then they have to be initialized somewhere. What are your thoughts on this?

My only syntactic quibble: constructor is so inconveniently long. I've argued in the past for new as special syntax in class bodies that indicates a constructor.

Agree.

# Russell Leggett (9 years ago)

On Tue, Mar 20, 2012 at 2:55 PM, David Herman <dherman at mozilla.com> wrote:

I can mostly dig it -- I've been trying to encourage minimal classes for quite some time now.

The part that I'm least happy about is that this doesn't allow for declarative private methods, because if we had the computed syntax like what we're adding to object literals, it would clash with hoisting:

{
    let priv = new Name();
    class C {
        // oops, priv is still undefined!!
        [priv](x, y, z) {
            return x * y / z
        }
    }
}

But I suppose the answer to that would eventually be better declarative syntax for private, as we've discussed but deferred for post-ES6 consideration.

Yes, better private support would be nice. There is still time for consensus on that that, but I still vote for this over nothing. Remember - safety syntax! (I know, everyone will tire of me saying that)

I do think that class declarations should be hoisted, both in the sense of being in scope for the whole containing block (as with let and function) and in the sense of being initialized before the block begins executing (as with function).

I agree, I think it would be counterintuitive otherwise. Hopefully this will not hold it back. Unfortunately this is one issue which we can't really put off because of the backwards compatibility monster.

My only syntactic quibble: constructor is so inconveniently long. I've argued in the past for new as special syntax in class bodies that indicates a constructor.

  • short & sweet
  • consistent with get/set shorthand
  • already a keyword so it can be given special treatment
  • doesn't have to actually correspond to a property called "new", so no extra magic properties on top of .constructor
  • can still create a .constructor property as well

Yes, I debated about this. In fact, I almost did go with new. Personally, I'm fine with either. I think what swung me in favor of "constructor" was:

  • other people seem to be happy with it, and that's what I'm shooting for here
  • CoffeeScript uses it. I don't really use CS, but it seemed short enough for them.
  • anyone making an additional .constructor is doing something bad, or maybe I just can't think of a good reason.

Let's chalk this one down as a bike shedding issue that won't hold it back.

# Kevin Smith (9 years ago)

class Snake extends Animal {

constructor(name){

    super(name);

}

Another option here would be:

class Snake extends Animal { // Using "new" ; ) new(name) : super(name) {} };

Note the call to the super constructor before the body of the constructor. This is similar to C#. The reason that we might want to go that way is because if we wanted to have instance initializers in the class body (a very common thing in class-based languages), then we'd want to maintain this order:

  1. Call superclass constructor
  2. Execute instance property initializers in text order
  3. Execute constructor body
# Rick Waldron (9 years ago)

Another option here would be:

class Snake extends Animal { // Using "new" ; ) new(name) : super(name) {} };

This could be visually mistaken for an object literal.

# Russell Leggett (9 years ago)

On Tue, Mar 20, 2012 at 3:30 PM, Kevin Smith <khs4473 at gmail.com> wrote:

class Snake extends Animal {

constructor(name){

    super(name);

}

Another option here would be:

class Snake extends Animal { // Using "new" ; ) new(name) : super(name) {} };

Note the call to the super constructor before the body of the constructor. This is similar to C#. The reason that we might want to go that way is because if we wanted to have instance initializers in the class body (a very common thing in class-based languages), then we'd want to maintain this order:

  1. Call superclass constructor
  2. Execute instance property initializers in text order
  3. Execute constructor body

Yeah - Java enforces this by making it an error if a call to super happens after any other statement. I think it makes sense, but does it belong in the safety syntax? Let's keep anything innovative out - that will inevitably lead to controversy.

# Russell Leggett (9 years ago)

On Tue, Mar 20, 2012 at 3:34 PM, Rick Waldron <waldron.rick at gmail.com>wrote:

Another option here would be:

class Snake extends Animal { // Using "new" ; ) new(name) : super(name) {} };

This could be visually mistaken for an object literal.

Did I mention controversy? :) Also - it breaks the nice overlap with object literal syntax (even with extensions). Right now we have a nice restriction that means we could technically go either way with the class body. I think that's probably the easiest way to get this through.

# Herby Vojčík (9 years ago)

David Herman wrote:

I can mostly dig it -- I've been trying to encourage minimal classes for quite some time now.

The part that I'm least happy about is that this doesn't allow for declarative private methods, because if we had the computed syntax like what we're adding to object literals, it would clash with hoisting:

{ let priv = new Name(); class C { // oops, priv is still undefined!! [priv](x, y, z) { return x * y / z } } }

Maybe too brittle, but since (I see that) classes are sugar for creating a function with appropriate [[Prototype]] and .prototype and_also populating the prototype, you can do hoisting the same way as you do for other elements (have a dead zone, hoist declaration, do not hoist prototype population), so that it would de-facto be:

{ let priv; let /* function */ C; // sort-of priv = new Name; C = function (/ctr_params/) /extends C_superclass/ {/ctr_body/}; C.prototype.{ [priv](x, y, z) { return x * y / z } } }

which works.

# Herby Vojčík (9 years ago)

Russell Leggett wrote:

On Tue, Mar 20, 2012 at 2:55 PM, David Herman <dherman at mozilla.com <mailto:dherman at mozilla.com>> wrote:

Yes, I debated about this. In fact, I almost did go with new. Personally, I'm fine with either. I think what swung me in favor of "constructor" was:

  • other people seem to be happy with it, and that's what I'm shooting for here
  • CoffeeScript uses it. I don't really use CS, but it seemed short enough for them.
  • anyone making an additional .constructor is doing something bad, or maybe I just can't think of a good reason.

Well, I favour constructor because it blends well with the underlying mechanics... there is a property named constructor in a prototype, pointing exactly to the constructor method of that prototype. So if I looks at the class {...} block as a blueprint for how the .prototype of the class will look like, it is the right name.

# Russell Leggett (9 years ago)

On Tue, Mar 20, 2012 at 3:55 PM, Herby Vojčík <herby at mailbox.sk> wrote:

Russell Leggett wrote:

On Tue, Mar 20, 2012 at 2:55 PM, David Herman <dherman at mozilla.com <mailto:dherman at mozilla.com>> wrote:

Yes, I debated about this. In fact, I almost did go with new. Personally, I'm fine with either. I think what swung me in favor of "constructor" was:

  • other people seem to be happy with it, and that's what I'm shooting for here
  • CoffeeScript uses it. I don't really use CS, but it seemed short enough for them.
  • anyone making an additional .constructor is doing something bad, or maybe I just can't think of a good reason.

Well, I favour constructor because it blends well with the underlying mechanics... there is a property named constructor in a prototype, pointing exactly to the constructor method of that prototype. So if I looks at the class {...} block as a blueprint for how the .prototype of the class will look like, it is the right name.

Haha, yes - that too. Forgot to mention it!

# Kevin Smith (9 years ago)

Yeah - Java enforces this by making it an error if a call to super happens after any other statement.

Yes - and "magically" interleaving instance field initializers in between the super call and the rest of the constructor body. Taking this approach would make desugaring more complicated. I'm not "voting" either way though.

# Russell Leggett (9 years ago)

On Tue, Mar 20, 2012 at 3:58 PM, Kevin Smith <khs4473 at gmail.com> wrote:

Yeah - Java enforces this by making it an error if a call to super happens

after any other statement.

Yes - and "magically" interleaving instance field initializers in between the super call and the rest of the constructor body. Taking this approach would make desugaring more complicated. I'm not "voting" either way though.

I wasn't suggesting we do it the Java way, I was just pointing out that Java does something along these lines as well. Personally, I think JS is a little too freewheeling to have that kind of restriction. I say let the chips fall. Super can be called wherever in the constructor. Something like your syntax can be added later to make it easier to "do the right thing". I would say that there should be no more restrictions than what Allen has shown in his <| examples. I don't think he has mentioned any, so I wouldn't have any either.

# Herby Vojčík (9 years ago)

Russell Leggett wrote:

We would have to decide what happens if they don't supply a constructor. I think we would need a default, and in my mind there are 3 possibilities:

  1. Empty constructor - does nothing.
  2. Calls super with no args
  3. Calls super as a pass through

I'm fond of 3, but might be missing some controversial reason for one of the others. Personally, I would be willing to take any of them for the sake of classes.

        * to call super on a method you have to indicate the method
    super.move
        * while not obvious in the example, I would say the class
    body should
              o be its own construct, not just an object literal - this

-1. See more below.

                makes it easier to have methods without ,’s and be
                future-proof for whatever we might want later

I have a lot of reasons (gut instinct, too, but also some though
experiments on how things get powerful and concise things can be if
classes automaticall had all the possibilities of (potientially
revved-up) object literals [1]).
So I need to see a really strong argument for abandoning the cause
of "body of class should be the same as the body of object literal
(with possible little numbers of changes)".

Ok, lets put it this way - my safety syntax only allows constructor + methods, with no commas in between. If commas after methods can be elided in object literal extensions then we're still on solid ground, it could go either way. What I would argue, though, is that we don't define the class body as equivalent to an object literal, there is still too much controversy there.

So my -1 holds if you want to add anything that goes beyond the
object-literal syntax, and I'd give -0.66 in case where you would
restrict it too much.

If you restrict it to only hold constructor and methods, it is still
a compatible (though stripped) object-literal - you can have methods
there (constructor is nothing special, just a method named
'constructor'; the magic of class keyword grants it [[Construct]]
and .prototype created from {} block and according to object literal
syntax you can have methods there without comma), so this would be
-0.66.

Well then I guess its a -0.66 from you, but remember this is the safety syntax! The battle can rage on later.

Well, I'm ok with this. My primary concern is to be able to move on in "{...}" as object-literal(-extension) syntax. I was only imagine all reactions like "why they did not add ... there too - it's just logical?!" of future users... but it has its use (and compact minimal definotion) if it is to be used for and only for instance methods (constructor masking as a special one of them).

If the alternative is no class syntax, you will have to do this anyway. Isn't this proposal still better than nothing?

Yes, reaction above.

Yes, we can go in little steps... but lots of them we already have
(super expressions are already planned to be orthogonal to classes),
and I think the next small step should be 'extends' for function
declarations (which, unless <|, explicitly says "I am creating
double chain here, in the spirit of 'classical' class inheritance;
and if thing after extends is not constructor, I throw").

What would you rather have?

 function Snake(name) extends Animal {
     super(name);
 }.prototype.{
     move(){
         alert("Slithering...");
         super.move(5);
     }
 }

or

 class Snake extends Animal {
     constructor(name){
         super(name);
     }

     move(){
         alert("Slithering...");
         super.move(5);
     }
 }

It is not about rather, it is about having the former anyway, as base language construct, and having the latter also, building upon it... I can live without the latter, I think the more important (for the expressivity) is to have the former (which we have a special case of <|, but I do not like the special case there, and it is not there for function declarations, only for function expressions).

I heard a lot of voices that were telling something like "... and let us do X in a class construct" and I was always very uneasy. Whatever X is, it should be able to achieve "normally" in the language, not only thought class construct. Class construct should (I see it so) only integrate a pattern (but pattern that could live without it).

I don't know if I would write:

class Foo extends Bar { constructor (x) { ... } method1 () {} method2 () {} }.prototype.{ commonNum: 0, commonColl: [] }

or

function Foo (x) extends Bar { ... }.prototype.{ method1 () {} method2 () {} commonNum: 0, commonColl: [] }

# Russell Leggett (9 years ago)

I don't know if I would write:

class Foo extends Bar { constructor (x) { ... } method1 () {} method2 () {} }.prototype.{ commonNum: 0, commonColl: [] }

or

function Foo (x) extends Bar { ... }.prototype.{ method1 () {} method2 () {} commonNum: 0, commonColl: []

}

If it were me, I would question the use of putting either commonNum or

commonColl on the prototype. commonNum is not as big a deal, but commonColl is just asking for trouble. Modifying the commonColl array from an instance will affect all other instances, but replacing it from an instance will just mask it. If the first is the desired purpose, it would be better places outside the class and captured in a closure variable. If the latter is desired, it would be better to just put it inside the constructor function. Avoiding non-methods steers us clear of controversy and still gets us the 80%.

As for the rest,

function Foo() extends Bar {...}

can easily be written as

class Foo extends Bar {
    constructor(){...}
}

so if it came down to only one I would take class, because I think it hits a bigger sweet spot. I'm not opposed to both, though, and have written as much in other proposals.

# Erik Arvidsson (9 years ago)

On Mon, Mar 19, 2012 at 13:03, Russell Leggett <russell.leggett at gmail.com> wrote:

So what do you say people? Is it safe enough? One of the biggest arguments I’ve heard against rushing in a class syntax now is that once its in we have to keep supporting it. I say that this is small enough we won’t regret it, and makes it possible to do a lot more in the future. If something more substantial can be agreed on soon enough to make it into ES6, even better, but maybe we can at least have a backup plan.

+1

Where do I sign?

# Allen Wirfs-Brock (9 years ago)

Perhaps surprisingly to some, I can easily get behind this proposal (or even in front of it) as long as we stick to pretty much the absolute minimum requires (and design) as outline in Russell's original message. We need to think of this as declarative syntax for just the constructor function+populate the prototype methods pattern that is commonly used today. Nothing more than that. As I scanned other message in this thread, I found my eyes glazing over on the message that talk about any sort of substantial change or embellishment. If we go down that path, we will just get stuck in the swamp again. I think the only variations in Russell proposal that we should consider are things (if any) that can be clearly explained as being future hostile. (or any outright bugs...)

Regarding Dave's concern: On Mar 20, 2012, at 11:55 AM, David Herman wrote:

I can mostly dig it -- I've been trying to encourage minimal classes for quite some time now.

The part that I'm least happy about is that this doesn't allow for declarative private methods, because if we had the computed syntax like what we're adding to object literals, it would clash with hoisting:

{
    let priv = new Name();
    class C {
        // oops, priv is still undefined!!
        [priv](x, y, z) {
            return x * y / z
        }
    }
}

Dave, It shouldn't be an issue. As has been discussed in other threads (and at least one TC39 F2F) hoisting class definitions and immediately initializing them the way we do with functions just doesn't work. Well, more precisely, it doesn't work for full featured class declarations with things like initialized data properties (class or instance side). Maybe we could get away with it with this limited form but then that would be future hostile to future extensions that try to grow it into a full bells and whistles class declaration.

We can live quite nicely with treating class declaration like const declarations. The name is logically hoisted so it is visible over the lexical scope, but the name is temporally dead until the class declaration is executed, in statement sequence, as an initializer. That means that we can have circular references between classes and forward references in inner functions to classes. The class declaration just need to be initialized before any such reference is actually evaluated. Its TDZ for classes.

Practically, that means that super classes declarations have to appear in the text before subclasses and that you probably can't tuck all your class declaration at the end of the block/function (yuck, anyway) like you can with function declarations. I think we can live fine with that.

Regarding privates, if classes are initialized in that manner there shouldn't be any problem with the pattern you use for in your example above. It works exactly the same as for object literals.

But I suppose the answer to that would eventually be better declarative syntax for private, as we've discussed but deferred for post-ES6 consideration.

I do think that class declarations should be hoisted, both in the sense of being in scope for the whole containing block (as with let and function) and in the sense of being initialized before the block begins executing (as with function).

My only syntactic quibble: constructor is so inconveniently long. I've argued in the past for new as special syntax in class bodies that indicates a constructor.

Well, it is defining the value of the constructor property. New syntax (eg, new) could always be added latter. I don't think we should risk derailing on it now.

  • short & sweet
  • consistent with get/set shorthand
  • already a keyword so it can be given special treatment
  • doesn't have to actually correspond to a property called "new", so no extra magic properties on top of .constructor
  • can still create a .constructor property as well

So here is my one possibly future hostile (or we need to decide now) issue:

class A {}; A.classProperty="A"; class B extends A {}; Console.log(B.classProperty); //"A" or undefined??

I don't believe Russell said, one way or the other. Potentially either way could be seen as future hostile if you think the "right" answer is the other one (and we have to do something in this regard). Personally I think the answer should be "A" which implies that we have class-side inheritance. This is a departure from current practice but because "classes are functions" there is no way in ES<=5.1 to set up class side inheritance other than by mutating proto.

We could make an accommodation that this only occurs if the "superclass" object is a function. Otherwise, the "superclass" is only uses as the [[Prototype]] of the prototype and the constructor function simply inherits from Function prototype. BTW, I assume the thing the the right of extends is an expression.

So, if you could say: class B extends A.prototype {}

if you don't want the class-side inheritance

To wrap up, this look really promising if we can restrict the feature scope in the manner Russell suggests. Hey, I could knock this off and have it in the spec. draft in a couple days work. There just isn't that much to it.

# Kevin Smith (9 years ago)

We can live quite nicely with treating class declaration like const declarations. The name is logically hoisted so it is visible over the lexical scope, but the name is temporally dead until the class declaration is executed, in statement sequence, as an initializer.

This looks good to me.

Personally I think the answer should be "A" which implies that we have

class-side inheritance.

I'm still trying to form an opinion on this. Dart has no static method inheritance. In the spec, there's the following commentary:

Inheritance of static methods has little utility in Dart. Static methods cannot be overridden. Any required static function can be obtained from its declaring library, and there is no need to bring it into scope via inheritance. Experience shows that developers are confused by the idea of inherited methods that are not instance methods

I'd be interested to hear other viewpoints on the matter.

# Mark S. Miller (9 years ago)

I have always been suspicious of static method inheritance. Smalltalk-76 did not have it. Smalltalk-80 did. Although I only ever had hands on with Smalltalk-80, I always felt that Smalltalk-76 had this one right. I know other smalltalkers disagree.

I think in a de-novo language like Dart or Smalltalk at the time, they are at least plausible. So I find it interesting that Dart chose to avoid them. For JS, I think they are disastrous because of "this" confusions. Normally, when a JS method is "static", what we implicitly mean is that it is to be found on a constructor function, and that the method does not depend on its this-binding. For static method inheritance to make sense, many static methods would instead be written to be this-sensitive. This will cause common usage patterns to break.

For JS, because of its this-binding rules and the programming patterns that have adapted to these rules, I believe it is too late for static inheritance. But even if it weren't, I suspect it's a bad idea anyway.

# David Herman (9 years ago)

On Mar 20, 2012, at 6:59 PM, Allen Wirfs-Brock wrote:

On Mar 20, 2012, at 11:55 AM, David Herman wrote:

The part that I'm least happy about is that this doesn't allow for declarative private methods, because if we had the computed syntax like what we're adding to object literals, it would clash with hoisting:

We can live quite nicely with treating class declaration like const declarations. The name is logically hoisted so it is visible over the lexical scope, but the name is temporally dead until the class declaration is executed, in statement sequence, as an initializer. That means that we can have circular references between classes and forward references in inner functions to classes. The class declaration just need to be initialized before any such reference is actually evaluated. Its TDZ for classes.

Practically, that means that super classes declarations have to appear in the text before subclasses and that you probably can't tuck all your class declaration at the end of the block/function (yuck, anyway) like you can with function declarations. I think we can live fine with that.

Regarding privates, if classes are initialized in that manner there shouldn't be any problem with the pattern you use for in your example above. It works exactly the same as for object literals.

Y'know, I think I'm about sold. Somehow it just seemed obvious to me that classes should be pre-initialized like functions. But just about every time something seems obvious to me I'm wrong. I should learn to trust the opposite of my instincts. ;)

Seriously, though, your argument for TDZ makes sense to me.

My only syntactic quibble: constructor is so inconveniently long. I've argued in the past for new as special syntax in class bodies that indicates a constructor.

Well, it is defining the value of the constructor property. New syntax (eg, new) could always be added latter. I don't think we should risk derailing on it now.

Well, hang on now. The 'constructor' syntax is not "just the constructor property." It still carries special status; the semantics of a class says "look for the property called 'constructor' and make that the [[Call]] and [[Construct]] behavior of the class."

Regardless of whether we spell it 'constructor' or 'new' it requires special semantics that says "there's one distinguished method form in the body that determines the constructor of the class." The question is how we spell that. This is 99.9% a surface syntax question. Tou could argue that spelling it 'new' should define a ["new"] method, or a ["new"] method and a ["constructor"] method, or just a ["constructor"] method. If the latter, it's semantically identical to spelling it 'constructor'. But even if we chose one of the other two alternatives, the semantic differences here are minor, and the ergonomics of the syntax matter.

Look, it won't be the end of the world if we go with 'constructor'. This particular question won't derail classes. But let's not tax the ergonomics for what would be either a tiny or even non-existent semantic difference.

So here is my one possibly future hostile (or we need to decide now) issue:

class A {}; A.classProperty="A"; class B extends A {}; Console.log(B.classProperty); //"A" or undefined??

I don't believe Russell said, one way or the other. Potentially either way could be seen as future hostile if you think the "right" answer is the other one (and we have to do something in this regard).

Agreed; this is a decision we can't defer if we support extends.

Personally I think the answer should be "A" which implies that we have class-side inheritance. This is a departure from current practice but because "classes are functions" there is no way in ES<=5.1 to set up class side inheritance other than by mutating proto.

I always found this the more appealing, but then again, if I'm supposed to be going with the opposite of my instincts (see above), then maybe I should disagree with you. ;)

Seriously, though, Mark's concerns are valid. (On a semi-related note, I intend to try an alternative of the binary data API with no meta-classes.) We can work through this issue.

We could make an accommodation that this only occurs if the "superclass" object is a function. Otherwise, the "superclass" is only uses as the [[Prototype]] of the prototype and the constructor function simply inherits from Function prototype. BTW, I assume the thing the the right of extends is an expression.

So, if you could say: class B extends A.prototype {}

if you don't want the class-side inheritance

Wait, I don't see how that could work. If the RHS of extends is an arbitrary expression, and we allow the programmer the freedom to provide either a super-class or a super-prototype, how do we know what the prototype's prototype is? IOW, it's ambiguous whether to treat:

class B extends SOMEEXPRESSION { ... }

as:

B = do {
    function B(...) { ... }
    B.prototype = SOMEEXPRESSION <| { ... }
    B
}

or:

B = do {
    function B(...) { ... }
    B.prototype = SOMEEXPRESSION.prototype <| { ... }
    B
}
# Axel Rauschmayer (9 years ago)

I’d still consider support for private name objects and for static minimal, but indeed would not add anything else:

gist.github.com/1336846

# Herby Vojčík (9 years ago)

Allen Wirfs-Brock wrote:

We can live quite nicely with treating class declaration like const declarations. The name is logically hoisted so it is visible over the lexical scope, but the name is temporally dead until the class declaration is executed, in statement sequence, as an initializer. That means that we can have circular references between classes and forward references in inner functions to classes. The class declaration just need to be initialized before any such reference is actually evaluated. Its TDZ for classes.

+1 (I wrote similar thing)

...

So here is my one possibly future hostile (or we need to decide now) issue:

class A {}; A.classProperty="A"; class B extends A {}; Console.log(B.classProperty); //"A" or undefined??

I don't believe Russell said, one way or the other. Potentially either

He said "A" in answer to my post (where I assumed he means "A", I think it should be "A"):

[about Russell's proposal]: I silently assume here that extends creates, of course, double chain (constuctor inherits as well as prototype inherits, not just the latter)

Correct

way could be seen as future hostile if you think the "right" answer is the other one (and we have to do something in this regard). Personally I think the answer should be "A" which implies that we have class-side inheritance. This is a departure from current practice but because "classes are functions" there is no way in ES<=5.1 to set up class side inheritance other than by mutating proto.

We could make an accommodation that this only occurs if the "superclass" object is a function. Otherwise, the "superclass" is only uses as the [[Prototype]] of the prototype and the constructor function simply inherits from Function prototype. BTW, I assume the thing the the right of extends is an expression.

So, if you could say: class B extends A.prototype {}

Please, rather different syntax (or nothing) then different behaviour based on context. After all, this is the less usual case, and can be achieved by:

let B = A.prototype <| function () { ... }.prototype.{ ... }

# Herby Vojčík (9 years ago)

David Herman wrote:

On Mar 20, 2012, at 6:59 PM, Allen Wirfs-Brock wrote:

On Mar 20, 2012, at 11:55 AM, David Herman wrote:

My only syntactic quibble: constructor is so inconveniently long. I've argued in the past for new as special syntax in class bodies that indicates a constructor.

Well, it /is/ defining the value of the constructor property. New syntax (eg, new) could always be added latter. I don't think we should risk derailing on it now.

Well, hang on now. The 'constructor' syntax is not "just the constructor property." It still carries special status; the semantics of a class says "look for the property called 'constructor' and make that the [[Call]] and [[Construct]] behavior of the class."

Regardless of whether we spell it 'constructor' or 'new' it requires special semantics that says "there's one distinguished method form in the body that determines the constructor of the class." The question is how we spell that. This is 99.9% a surface syntax question. Tou could argue that spelling it 'new' should define a ["new"] method, or a ["new"] method and a ["constructor"] method, or just a ["constructor"] method. If the latter, it's semantically identical to spelling it 'constructor'. But even if we chose one of the other two alternatives, the semantic differences here are minor, and the ergonomics of the syntax matter.

One possibility is to use this:

class Foo extends Bar { @(...) { ... } // constructor @x(...) {...} // method with private name x die(...) {...} // method with public name }

The @() {} syntax embraces the fact that 'it is a special case thing'. It in fact mimics as 'a method with private name ""' which could be read as 'something inherently special for the class, so that it is stored under default private slot' (there is nothing like 'default private slot', I know, I'm just showig possible readability of it), that is, its constructor (hopefully at least someone understands what I mean).

# Russell Leggett (9 years ago)

On Wed, Mar 21, 2012 at 7:03 AM, Herby Vojčík <herby at mailbox.sk> wrote:

Allen Wirfs-Brock wrote:

We can live quite nicely with treating class declaration like const declarations. The name is logically hoisted so it is visible over the lexical scope, but the name is temporally dead until the class declaration is executed, in statement sequence, as an initializer. That means that we can have circular references between classes and forward references in inner functions to classes. The class declaration just need to be initialized before any such reference is actually evaluated. Its TDZ for classes.

+1 (I wrote similar thing)

...

So here is my one possibly future hostile (or we need to decide now) issue:

class A {}; A.classProperty="A"; class B extends A {}; Console.log(B.classProperty); //"A" or undefined??

I don't believe Russell said, one way or the other. Potentially either

He said "A" in answer to my post (where I assumed he means "A", I think it should be "A"):

[about Russell's proposal]: I silently assume here that extends

creates, of course, double chain (constuctor inherits as well as prototype inherits, not just the latter)

Correct

Yeah - that is the way I was leaning, partly because I like it, and partly because that was the behavior in <| for functions in the class pattern. I think it could help make it easier for people to fill the gaps in the minimal syntax. For example, many class libs have some mixin functionality. If mixin were added to a custom base class, then you could do:

class Foo extends MyBase {
    ...
}.mixin(enumerable);

Thats not the only way to get the job done, so its not really critical. I think Mark's concerns are valid about using "this" at the class level to take advantage of the static inheritance, but ultimately I think its a useful tool. If people don't use it, I don't think it will really add much cognitive load. On the other hand, I think allowing it will really let JavaScript shine as a prototype language. Statics in other languages like Java are frowned upon (in some circles) precisely because they aren't very Object Oriented. You can't inherit or override them like you can instance methods. I think this would be advantage JS. In the spirit of safety syntax though, I personally could go either way. I think its nice, but not critical, and shouldn't derail classes.

way could be seen as future hostile if you think the "right" answer is

the other one (and we have to do something in this regard). Personally I think the answer should be "A" which implies that we have class-side inheritance. This is a departure from current practice but because "classes are functions" there is no way in ES<=5.1 to set up class side inheritance other than by mutating proto.

We could make an accommodation that this only occurs if the "superclass" object is a function. Otherwise, the "superclass" is only uses as the [[Prototype]] of the prototype and the constructor function simply inherits from Function prototype. BTW, I assume the thing the the right of extends is an expression.

So, if you could say: class B extends A.prototype {}

Please, rather different syntax (or nothing) then different behaviour based on context. After all, this is the less usual case, and can be achieved by:

let B = A.prototype <| function () { ... }.prototype.{ ...

}

Yeah, this was not an assumption I had been making. I had, in fact, been thinking the opposite. To the right of extends would always be a constructor function. Being more restrictive on it now should not be future hostile, and is less controversial.

# Kevin Smith (9 years ago)

We should probably hold off on private and static member syntax until there's consensus on the other open issues:

  • "constructor" vs. "new" (Dave and I say "new", Allen says "constructor"; mostly aesthetic - can be put off for now)
  • class-side inheritance? (Mark and I say no, Allen and Russell say yes)
  • What restrictions for RHS of "extends"? Must it be a constructor? (Russell and I say yes)

Additionally, I'm still worried about how we call the superclass constructor:

new() {
    // do arbitrary stuff here
    super(...);
}

This would potentially allow a superclass instance method to be called before the superclass initialization has completed. If we want to maintain the invariant (as it seems most class-based languages do) that methods can only be called after initialization has completed, then this won't work. Thoughts?

# Russell Leggett (9 years ago)

On Wed, Mar 21, 2012 at 9:35 AM, Kevin Smith <khs4473 at gmail.com> wrote:

Hi Axel,

We should probably hold off on private and static member syntax until there's consensus on the other open issues:

Yeah, we can't add any features on the safety syntax, we'll get back to where we started. Kevin summarized the remaining issues really well, and they seem very tractable with a little debate.

  • "constructor" vs. "new" (Dave and I say "new", Allen says "constructor"; mostly aesthetic - can be put off for now)

I've thought about it and I'm pretty solid on constructor now:

  1. I think its easier to explain - it will actually result in a constructor on the prototype.
  2. The actual constructor function and the .constructor property really should always be in sync - this helps with that.
  3. "new" doesn't have those benefits - people might expect to be able to call .new() like in ruby.
  4. "new" conflicts with the new <object> proposal.
  • class-side inheritance? (Mark and I say no, Allen and Russell say yes)
  • What restrictions for RHS of "extends"? Must it be a constructor?

(Russell and I say yes)

I've already made my arguments here

Additionally, I'm still worried about how we call the superclass constructor:

new() {
    // do arbitrary stuff here
    super(...);
}

This would potentially allow a superclass instance method to be called before the superclass initialization has completed. If we want to maintain the invariant (as it seems most class-based languages do) that methods can only be called after initialization has completed, then this won't work. Thoughts?

I think that's ok. Just like I don't think there should be an implicit super call at the top of the constructor if you skip it. Sure Java and C# enforce this, but I don't think its really the JavaScript style. There are realistic use cases for doing something before you call super, or skipping a call to super. It is also something that can easily be caught by a linter or IDE as a warning.

# Kevin Smith (9 years ago)
  1. I think its easier to explain - it will actually result in a constructor on the prototype.
  2. The actual constructor function and the .constructor property really should always be in sync - this helps with that.
  3. "new" doesn't have those benefits - people might expect to be able to call .new() like in ruby.
  4. "new" conflicts with the new <object> proposal.

There are some minor practical considerations on the "constructor" side,

and aesthetic considerations on the "new" side. Instead of squaring off now, can we just agree to leave if off the table temporarily?

I think that's ok. Just like I don't think there should be an implicit

super call at the top of the constructor if you skip it. Sure Java and C# enforce this, but I don't think its really the JavaScript style. There are realistic use cases for doing something before you call super, or skipping a call to super.

I can basically agree with you (although I don't see a use case for skipping the super constructor). But because of ordering requirements this is going to shut the door forever on instance field initializers. Nothing necessarily wrong with that, we just need to be aware.

# Russell Leggett (9 years ago)

On Wed, Mar 21, 2012 at 11:12 AM, Kevin Smith <khs4473 at gmail.com> wrote:

  1. I think its easier to explain - it will actually result in a constructor on the prototype.
  2. The actual constructor function and the .constructor property really should always be in sync - this helps with that.
  3. "new" doesn't have those benefits - people might expect to be able to call .new() like in ruby.
  4. "new" conflicts with the new <object> proposal.

There are some minor practical considerations on the "constructor" side, and aesthetic considerations on the "new" side. Instead of squaring off now, can we just agree to leave if off the table temporarily?

Agreed, table it.

I think that's ok. Just like I don't think there should be an implicit

super call at the top of the constructor if you skip it. Sure Java and C# enforce this, but I don't think its really the JavaScript style. There are realistic use cases for doing something before you call super, or skipping a call to super.

I can basically agree with you (although I don't see a use case for skipping the super constructor). But because of ordering requirements this is going to shut the door forever on instance field initializers. Nothing necessarily wrong with that, we just need to be aware.

I think there's probably still wiggle room. Perhaps in the future, new() could be added with additional semantics... Anyway, unless we are going to really make classes distinct from functions, I'm not sure how we could enforce super in the way you describe without affecting it in other forms. Can you only extend classes created using "class" syntax?

# Kevin Smith (9 years ago)

I think there's probably still wiggle room. Perhaps in the future, new() could be added with additional semantics...

This may be a possibility. Presumably in such a future it would be a static error to have both a "constructor" and a "new".

Anyway, unless we are going to really make classes distinct from functions, I'm not sure how we could enforce super in the way you describe without affecting it in other forms. Can you only extend classes created using "class" syntax?

No - any function (constructor) can be to the right of "extends". Again, not necessarily arguing the case either way, but if in some future there were field initializers then they would presumably be at the top of the desugared constructor:

function _ctor(a, b, c) { // super constructor call? // execute field initializers // "new" / "constructor" body }

# Russell Leggett (9 years ago)

Anyway, unless we are going to really make classes distinct from functions, I'm not sure how we could enforce super in the way you describe without affecting it in other forms. Can you only extend classes created using "class" syntax?

No - any function (constructor) can be to the right of "extends". Again, not necessarily arguing the case either way, but if in some future there were field initializers then they would presumably be at the top of the desugared constructor:

function _ctor(a, b, c) { // super constructor call? // execute field initializers // "new" / "constructor" body }

Yeah, I guess I just meant that we still have to handle old ways of wiring up classes. Lets say we made a class with field initializers and then extended it with a function, but didn't use the class syntax, wouldn't that break the integrity you're trying to reinforce? I just think its a ball of wax that we can deal with later, and leave this as more sugar over existing semantics.

# Allen Wirfs-Brock (9 years ago)

On Mar 20, 2012, at 11:32 PM, David Herman wrote:

On Mar 20, 2012, at 6:59 PM, Allen Wirfs-Brock wrote:

On Mar 20, 2012, at 11:55 AM, David Herman wrote:

My only syntactic quibble: constructor is so inconveniently long. I've argued in the past for new as special syntax in class bodies that indicates a constructor.

Well, it is defining the value of the constructor property. New syntax (eg, new) could always be added latter. I don't think we should risk derailing on it now.

Well, hang on now. The 'constructor' syntax is not "just the constructor property." It still carries special status; the semantics of a class says "look for the property called 'constructor' and make that the [[Call]] and [[Construct]] behavior of the class."

Actually, the semantics is probably more like: the value bound to the <classMame> is the function object defined by the <methodDefinition> whose <propertyName> is "constructor".

Regardless of whether we spell it 'constructor' or 'new' it requires special semantics that says "there's one distinguished method form in the body that determines the constructor of the class."

There is one distinguished method that is the class object (aka, the constructor function). A class declaration essentially desugars into the definition of a constructor function and a object that is the prototype associated with that constructor.

The question is how we spell that. This is 99.9% a surface syntax question. Tou could argue that spelling it 'new' should define a ["new"] method, or a ["new"] method and a ["constructor"] method, or just a ["constructor"] method. If the latter, it's semantically identical to spelling it 'constructor'. But even if we chose one of the other two alternatives, the semantic differences here are minor, and the ergonomics of the syntax matter.

You need to drop the [ ]'s (although I'm not sure what you meant by them...

The existing "class pattern" in ES<=5.1 says that we need to have a property named "constructor" on the prototype object whose value is the actual constructor function (which is also the object that is bound to the class name). Even if we used new as a keyword to designate the declaration that provides the body of the constructor function we would still have to create a property on the prototype named "constructor". We could do this but it opens up other others just has what if we use "new" to designate the constructor function declaration and "constructor" is explicitly used as the name of one of methods?

We could deal with those issue and I'm not particularly opposed to using "new" as a designator. However the reason I pushed back against considering it was that I don't want this to become the sort of issue that derails this whole approach. On the other hand, it does seem like a decision we have to make now, if ever. If we don't give "new" special meaning now then people can define methods named "new" (on the prototype) and backwards compatibility would prevent us for giving "new" special meaning in the future.

Look, it won't be the end of the world if we go with 'constructor'. This particular question won't derail classes. But let's not tax the ergonomics for what would be either a tiny or even non-existent semantic difference.

So here is my one possibly future hostile (or we need to decide now) issue:

class A {}; A.classProperty="A"; class B extends A {}; Console.log(B.classProperty); //"A" or undefined??

I don't believe Russell said, one way or the other. Potentially either way could be seen as future hostile if you think the "right" answer is the other one (and we have to do something in this regard).

Agreed; this is a decision we can't defer if we support extends.

Personally I think the answer should be "A" which implies that we have class-side inheritance. This is a departure from current practice but because "classes are functions" there is no way in ES<=5.1 to set up class side inheritance other than by mutating proto.

I always found this the more appealing, but then again, if I'm supposed to be going with the opposite of my instincts (see above), then maybe I should disagree with you. ;)

I would guess that your instinctive response comes from thinking about a "class" is something more than just a composite of objects. We can talk more about this later after I respond to Mark

Seriously, though, Mark's concerns are valid. (On a semi-related note, I intend to try an alternative of the binary data API with no meta-classes.) We can work through this issue.

We could make an accommodation that this only occurs if the "superclass" object is a function. Otherwise, the "superclass" is only uses as the [[Prototype]] of the prototype and the constructor function simply inherits from Function prototype. BTW, I assume the thing the the right of extends is an expression.

So, if you could say: class B extends A.prototype {}

if you don't want the class-side inheritance

Wait, I don't see how that could work. If the RHS of extends is an arbitrary expression, and we allow the programmer the freedom to provide either a super-class or a super-prototype, how do we know what the prototype's prototype is? IOW, it's ambiguous whether to treat:

If the value of SOMEEXPRESSION is a constructor function (typeof == "function" && and has a "prototype" property) then the new constructor inherits from SOMEEXPRESSION and the new prototype inherits from SOMEEXPRESSION.prototype. Otherwise, the new consructor inherits from Function.prototype and the new prototype inherits from SOMEEXPRESSION. That is essentially the semantics I've defined for SOMEEXPRESSION <| function () {}

class B extends SOMEEXPRESSION { ... }

as:

B = do {
    function B(...) { ... }
    B.prototype = SOMEEXPRESSION <| { ... }
    B
}

or:

B = do {
    function B(...) { ... }
    B.prototype = SOMEEXPRESSION.prototype <| { ... }
    B
}

B = do{ let B = SOMEEXPRESSION <| function B(...) { ...}; B.prototype.{...} }

(or do avoid Dave gagging on .{ ;-)

B = do{ let B = SOMEEXPRESSION <| function B(...) { ...}; B.prototype= SOMEEXPRESSION.prototype <| {constructor: B, ...} B }

(or to be maximally explicit}

B= do { let B = function B(...) {...}; if (typeof SOMEEXPRESSION == "function" && typeof SOMEEXPRESSION.prototype == "object") B.proto=SOMEEXPRESSION; B.prototype= SOMEEXPRESSION.prototype <| {constructor: B, ...} B }

# Allen Wirfs-Brock (9 years ago)

On Mar 20, 2012, at 10:32 PM, Mark S. Miller wrote:

I have always been suspicious of static method inheritance. Smalltalk-76 did not have it. Smalltalk-80 did. Although I only ever had hands on with Smalltalk-80, I always felt that Smalltalk-76 had this one right. I know other smalltalkers disagree.

First off, it is wrong to call these "static methods". There is nothing static about them. We are simply talking about regular dynamic properties of a function object. By using the term "static" you are making an association with a different concept that comes statically typed languages where "classes" are not first class objects. JavaScript is not that kind of language and it is wrong to draw inferences from them. I suggest that we call these "class properties" or "function properties".

Rather than using Java/C++ for a model and terminology for classes we should be looking at successful class-based dynamic OO languages.

Smalltalk-80 added them because they discovered that having a single set of behaviors for all class objects was too constraining. Ruby also supports class methods with inheritance. [1] seems like a reasonable tutorial on using class methos in Ruby.

[1] www.rubyfleebie.com/understanding-class-methods-in-ruby

I think in a de-novo language like Dart or Smalltalk at the time, they are at least plausible. So I find it interesting that Dart chose to avoid them. For JS, I think they are disastrous because of "this" confusions. Normally, when a JS method is "static", what we implicitly mean is that it is to be found on a constructor function, and that the method does not depend on its this-binding. For static method inheritance to make sense, many static methods would instead be written to be this-sensitive. This will cause common usage patterns to break.

There is no reason for this confusion. It think you must be coming from thinking about class methods as "static". Classes (functions) are just objects and object have properties some of which have function values. We call such properties methods. When a method is invoked on an object (eg, foo.method()) the this minding is the target object of the invocation (eg, foo). That is true regardless of whether foo is an "instance", or a function, or a class. It is simply how object-oriented invocation works.

I don't know where you got the "and that method does not depend on its this-binding" from. It certainly isn't a given and it isn't the convention in dynamic class based languages.

If you define

Array.from = function(collection) {
      ...
      this
      ...
}

and call it Array.from(someCollection)

withi the activation of that function the value of |this| is going to be Array. There is no way around it. There are plenty of good reasons to use this. Consider a naive implementation of this function

Array.from = function(collection) {
      let a = new Array;
      for (let v of collection) a.push(v);
      return a;
}

I say this is naive because it has a significant problem. Array methods are generally "generic" so what if we just assume that the from method is also generic and installed in on some other "class":

MyArrayLike = Array.from; MyArrayLike.prototype = Array.prototype.push; //push really is generic and will work as a method on any object

What do we get if we then say:

MyArrayLIke.from(myCollection) instanceof MyCollection     //expect true, but get false

This wouldn't be the case if we had properly coded from using this:

Array.from = function(collection) {
      let a = new this;   // <=============  this, not Array
      for (let v of collection) a.push(v);
      return a;
}

This same idea applies if instead of copying a reference to the from function we had inherited it directly from Array:

class SpecializedArray extends Array { ...};

We should be able to say:

 let s = SpecializedArray.from([1,2,3,4,5]);

an get an instance of SpecializedArray. Referencing this in class methods is the key to making this all work.

For JS, because of its this-binding rules and the programming patterns that have adapted to these rules,

JS this binding rules are absolutely no different from Smalltalk, Ruby, or most any other OO language. The only thing that is different about JS is that it makes it (too) easy to extract a method as a function without an object association and then to invoke that function with an arbitrary (or default) this value (as if, the function was installed as a method on the this value object). I don't see how that latter fact has any relevance to this discussion. If you extract methods you need to know what requirements they impose upon their this value. This applies whether the method was defined as an instance method, a class method, or just as a naked function.

I believe it is too late for static inheritance. But even if it weren't, I suspect it's a bad idea anyway.

I'm quite certain that class-side inheritance is a good idea for dynamic class based OO languages with inheritance. It has proven utility. There may be things about that kind of language that an individual might not like. They might object to dynamic, or class-based, or OO, or inheritance. But if you have that combination, inheritable class methods make a lot of sense.

# Allen Wirfs-Brock (9 years ago)

On Mar 21, 2012, at 6:25 AM, Russell Leggett wrote:

...

Yeah - that is the way I was leaning, partly because I like it, and partly because that was the behavior in <| for functions in the class pattern. I think it could help make it easier for people to fill the gaps in the minimal syntax. For example, many class libs have some mixin functionality. If mixin were added to a custom base class, then you could do:

class Foo extends MyBase {
    ...
}.mixin(enumerable);

There are likely issue with the above syntax. However, I can imagine someway saying:

class Foo extends mixin(MyBase, enumerable) { }

where mixin is defined as:

function mixin(directSuper, ...mixins) { let effectiveSuper = class extends directSuper{ constructor(...args) {return super.constructor(...args)}}; for (let m of mixins) Object.extend(effectiveSuper.proto,mixins); //probably need some conflict resolution logic return effectiveSuper }

# Russell Leggett (9 years ago)

On Wed, Mar 21, 2012 at 2:25 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

On Mar 21, 2012, at 6:25 AM, Russell Leggett wrote:

...

Yeah - that is the way I was leaning, partly because I like it, and partly because that was the behavior in <| for functions in the class pattern. I think it could help make it easier for people to fill the gaps in the minimal syntax. For example, many class libs have some mixin functionality. If mixin were added to a custom base class, then you could do:

class Foo extends MyBase {
    ...
}.mixin(enumerable);

There are likely issue with the above syntax. However, I can imagine someway saying:

class Foo extends mixin(MyBase, enumerable) { }

where mixin is defined as:

function mixin(directSuper, ...mixins) { let effectiveSuper = class extends directSuper{ constructor(...args) {return super.constructor(...args)}}; for (let m of mixins) Object.extend(effectiveSuper.proto,mixins); //probably need some conflict resolution logic return effectiveSuper }

hmmm... yeah, I like this a lot, actually.

# Allen Wirfs-Brock (9 years ago)

On Mar 21, 2012, at 6:35 AM, Kevin Smith wrote:

Hi Axel,

We should probably hold off on private and static member syntax until there's consensus on the other open issues:

  • "constructor" vs. "new" (Dave and I say "new", Allen says "constructor"; mostly aesthetic - can be put off for now)
  • class-side inheritance? (Mark and I say no, Allen and Russell say yes)
  • What restrictions for RHS of "extends"? Must it be a constructor? (Russell and I say yes)

Additionally, I'm still worried about how we call the superclass constructor:

new() {
    // do arbitrary stuff here
    super(...);
}

This would potentially allow a superclass instance method to be called before the superclass initialization has completed. If we want to maintain the invariant (as it seems most class-based languages do) that methods can only be called after initialization has completed, then this won't work. Thoughts?

This invariant can is difficult, if not impossible to maintain. Even the concept of "initialization has completed" is difficult to define. My experience is 10 years out of date, but I know that Java was not successful at maintaining that invariant.

Note that this invariant is probably most important for static languages that depend upon initialization invariants for memory safety. ES does not have this issue and the langue runtime level, although there could of course be application level invariants that are violated.

It is actually sometimes quite useful to do some processing before invoking super initialization:

constructor (...args) { let processedArgs = preprocessToBeAcceptableForSuper(args); super(processedArgs); ... }

My sense is that this invariant is a very minor issue some language designers send a lot of time worry about either come up with a solution that really does n'twork or which eliminates more useful usage patterns than than elimiante hazards.

Both Ruby and Smalltalk support arbitrary placement of super initialization calls (if any) and at least for Smalltalk is was not a major source of problems..

# Allen Wirfs-Brock (9 years ago)

On Mar 21, 2012, at 8:41 AM, Russell Leggett wrote:

On Wed, Mar 21, 2012 at 11:12 AM, Kevin Smith <khs4473 at gmail.com> wrote: I think its easier to explain - it will actually result in a constructor on the prototype. The actual constructor function and the .constructor property really should always be in sync - this helps with that. "new" doesn't have those benefits - people might expect to be able to call .new() like in ruby. "new" conflicts with the new <object> proposal. There are some minor practical considerations on the "constructor" side, and aesthetic considerations on the "new" side. Instead of squaring off now, can we just agree to leave if off the table temporarily?

Agreed, table it.

We have to make a decision or a spec. can't be written. There needs to at least be a stake in the ground. it looks to me like that stake is "constructor"

# Russell Leggett (9 years ago)

On Wed, Mar 21, 2012 at 2:47 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

On Mar 21, 2012, at 8:41 AM, Russell Leggett wrote:

On Wed, Mar 21, 2012 at 11:12 AM, Kevin Smith <khs4473 at gmail.com> wrote:

  1. I think its easier to explain - it will actually result in a constructor on the prototype.
  2. The actual constructor function and the .constructor property really should always be in sync - this helps with that.
  3. "new" doesn't have those benefits - people might expect to be able to call .new() like in ruby.
  4. "new" conflicts with the new <object> proposal.

There are some minor practical considerations on the "constructor" side, and aesthetic considerations on the "new" side. Instead of squaring off now, can we just agree to leave if off the table temporarily?

Agreed, table it.

We have to make a decision or a spec. can't be written. There needs to at least be a stake in the ground. it looks to me like that stake is "constructor"

Yes, I interpreted it as constructor for now, and we can argue it later.

# John J Barton (9 years ago)

On Wed, Mar 21, 2012 at 11:52 AM, Russell Leggett <russell.leggett at gmail.com> wrote:

On Wed, Mar 21, 2012 at 2:47 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

On Mar 21, 2012, at 8:41 AM, Russell Leggett wrote:

On Wed, Mar 21, 2012 at 11:12 AM, Kevin Smith <khs4473 at gmail.com> wrote:

I think its easier to explain - it will actually result in a constructor on the prototype. The actual constructor function and the .constructor property really should always be in sync - this helps with that.

So the proposal is that the class body declares a function called "constructor" in with other functions that will end up on the [[Prototype]] but the instance created from 'new' operating on the class function call will have an own property "constructor" that points to this function. Will the property also appear on the [[Prototype]] under 'constructor'?

jjb

# Axel Rauschmayer (9 years ago)

On Mar 21, 2012, at 6:35 AM, Kevin Smith wrote:

Hi Axel,

We should probably hold off on private and static member syntax until there's consensus on the other open issues:

  • "constructor" vs. "new" (Dave and I say "new", Allen says "constructor"; mostly aesthetic - can be put off for now)
  • class-side inheritance? (Mark and I say no, Allen and Russell say yes)
  • What restrictions for RHS of "extends"? Must it be a constructor? (Russell and I say yes)

Additionally, I'm still worried about how we call the superclass constructor: [...]

I’ve removed the static block. Sugar for private name objects is worth it, IMO. I consider the other issues orthogonal to my syntax sketch (I’m not saying that they are not important, they just don’t matter as much to me). Even whether or not it’ll be super(...), super.constructor(...) or something else.

# Herby Vojčík (9 years ago)

John J Barton wrote:

On Wed, Mar 21, 2012 at 11:52 AM, Russell Leggett <russell.leggett at gmail.com> wrote:

On Wed, Mar 21, 2012 at 2:47 PM, Allen Wirfs-Brock<allen at wirfs-brock.com> wrote:

On Mar 21, 2012, at 8:41 AM, Russell Leggett wrote:

On Wed, Mar 21, 2012 at 11:12 AM, Kevin Smith<khs4473 at gmail.com> wrote:

I think its easier to explain - it will actually result in a constructor on the prototype. The actual constructor function and the .constructor property really should always be in sync - this helps with that.

So the proposal is that the class body declares a function called "constructor" in with other functions that will end up on the [[Prototype]] but the instance created from 'new' operating on the class function call will have an own property "constructor" that points to this function. Will the property also appear on the [[Prototype]] under 'constructor'?

No, it is same as existing reality. Do not read "the .constructor property" from above as "own .constructor property" (which may have led to your conclusion) but as "shared-via-prototype .constructor property", which is exactly the current status quo.

That's the beauty of 'constructor' named prototype/instance method - the status quo is such that it already is there, though it is created nehind the scenes as the magic of making a function also a constructor. So by using it nothing new is added.

# Axel Rauschmayer (9 years ago)

For example (IIRC, that’s what Allen’s class operator does, I’ve also seen similar functions on the web):

function Class(proto) {
    let constr = proto.constructor;
    constr.prototype = proto;
    return constr;
}
let Point = Class({
    constructor: function (x, y) {
        this.x = x;
        this.y = y;
    }
});
# Axel Rauschmayer (9 years ago)

As Herby mentioned, the result of the function Class() conforms to the best practice of:

 C.prototype.constructor === C

(Given a constructor function C.) The above assertion holds for all built-ins.

# Erik Arvidsson (9 years ago)

On Wed, Mar 21, 2012 at 11:47, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

We have to make a decision or a spec. can't be written.  There needs to at least be a stake in the ground.  it looks to me like that stake is "constructor"

constructor++

For the record, we went through this exercise when we designed the Traceur classes. We started with "constrcutor", changed to "new" and eventually went back to "constructor". The main reason for "constructor" was that it was less confusing since we still have to have a "constructor" field and it is more consistent with ES3 de facto classes.

# Rick Waldron (9 years ago)

[snip] There are likely issue with the above syntax. However, I can imagine someway saying:

class Foo extends mixin(MyBase, enumerable) { }

where mixin is defined as:

function mixin(directSuper, ...mixins) { let effectiveSuper = class extends directSuper{ constructor(...args) {return super.constructor(...args)}}; for (let m of mixins) Object.extend(effectiveSuper.proto,mixins); //probably need some conflict resolution logic return effectiveSuper }

Allen, this is very exciting, thank you for point out the possibility. I'm going to try to work this into some of the support cases I've been writing.

# Kevin Smith (9 years ago)

I'm good with everything in your proposal, along with Allen's clarifications.

# Allen Wirfs-Brock (9 years ago)

I've created a strawman proposal for Russell's class "safety syntax": strawman:maximally_minimal_classes

# Kevin Smith (9 years ago)

Looks great. A couple of questions (I may have missed something):

1.) What is a generator method?

*PropertyName ( FormalParameterList? ) { FunctionBody } // generator

method

2.) What happens if the AssignmentExpression to the right of "extends" evaluates to something that doesn't have a "prototype" property?

# Allen Wirfs-Brock (9 years ago)

On Mar 22, 2012, at 10:05 AM, Kevin Smith wrote:

Looks great. A couple of questions (I may have missed something):

1.) What is a generator method?

*PropertyName ( FormalParameterList? ) { FunctionBody } // generator method

harmony:generators

2.) What happens if the AssignmentExpression to the right of "extends" evaluates to something that doesn't have a "prototype" property?

Same semantics as: expr <| function() {}

# Kevin Smith (9 years ago)

Ah - knew I'd missed something completely obvious -

# Kevin Smith (9 years ago)

Since the method name is specified as PropertyName, it will allow bracketed expressions for names, correct?

class A { maybePrivateMethod { ... } }

# Brendan Eich (9 years ago)

David Herman wrote:

I do think that class declarations should be hoisted, both in the sense of being in scope for the whole containing block (as with let and function) and in the sense of being initialized before the block begins executing (as with function).

Allen has argued against hoisting, based on the extends clause being an evaluated expression.

# Brendan Eich (9 years ago)

Rick Waldron wrote:

I've taken my previous tri-lambda-syntax support example (gist.github.com/2013909) and I've updated the code to use your proposed class syntax... It looks nice:

gist.github.com/2139330

This is sweet! Even without the thin-arrow function expression.

# Brendan Eich (9 years ago)

Russell Leggett wrote:

Well, I favour constructor because it blends well with the
underlying mechanics... there _is_ a property named constructor in
a prototype, pointing exactly to the constructor method of that
prototype. So if I looks at the class {...} block as a blueprint
for how the .prototype of the class will look like, it is the
right name.

Haha, yes - that too. Forgot to mention it!

I used to think this was convincing as a reason to prefer 'constructor' over 'new', but then Dave Herman pointed out that the syntax does not magically wire up the class binding or expression result to the 'constructor' property -- there's still a magic extra semantic attached to the name. Given this, we could choose a shorter name (and impute 'constructor' from it).

# Brendan Eich (9 years ago)

Brendan Eich wrote:

Allen has argued against hoisting, based on the extends clause being an evaluated expression.

Also (if included) based on static property initializers being evaluated, intuitively in declaration/statement order, but if you hoist the class when do you evaluate the statics' inits?

# Russell Leggett (9 years ago)

On Fri, Mar 23, 2012 at 12:30 AM, Brendan Eich <brendan at mozilla.org> wrote:

Brendan Eich wrote:

Allen has argued against hoisting, based on the extends clause being an evaluated expression.

Also (if included) based on static property initializers being evaluated, intuitively in declaration/statement order, but if you hoist the class when do you evaluate the statics' inits?

This is what Allen said about hoisting for this spec (Its been a long thread, not sure if you missed this.):

========== From Allen ============================ Regarding Dave's concern: On Mar 20, 2012, at 11:55 AM, David Herman wrote:

I can mostly dig it -- I've been trying to encourage minimal classes for quite some time now.

The part that I'm least happy about is that this doesn't allow for declarative private methods, because if we had the computed syntax like what we're adding to object literals, it would clash with hoisting:

{
    let priv = new Name();
    class C {
        // oops, priv is still undefined!!
        [priv](x, y, z) {
            return x * y / z
        }
    }
}

Dave, It shouldn't be an issue. As has been discussed in other threads (and at least one TC39 F2F) hoisting class definitions and immediately initializing them the way we do with functions just doesn't work. Well, more precisely, it doesn't work for full featured class declarations with things like initialized data properties (class or instance side). Maybe we could get away with it with this limited form but then that would be future hostile to future extensions that try to grow it into a full bells and whistles class declaration.

We can live quite nicely with treating class declaration like const declarations. The name is logically hoisted so it is visible over the lexical scope, but the name is temporally dead until the class declaration is executed, in statement sequence, as an initializer. That means that we can have circular references between classes and forward references in inner functions to classes. The class declaration just need to be initialized before any such reference is actually evaluated. Its TDZ for classes.

Practically, that means that super classes declarations have to appear in the text before subclasses and that you probably can't tuck all your class declaration at the end of the block/function (yuck, anyway) like you can with function declarations. I think we can live fine with that.

Regarding privates, if classes are initialized in that manner there shouldn't be any problem with the pattern you use for in your example above. It works exactly the same as for object literals.

Dave responded with being pretty happy about that resolution:

"Y'know, I think I'm about sold. Somehow it just seemed obvious to me that classes should be pre-initialized like functions. But just about every time something seems obvious to me I'm wrong. I should learn to trust the opposite of my instincts. ;)

Seriously, though, your argument for TDZ makes sense to me."

FWIW, it sits well with me also.

# Brendan Eich (9 years ago)

Russell Leggett wrote:

This is what Allen said about hoisting for this spec (Its been a long thread, not sure if you missed this.):

Thanks, I did catch up that far on the thread, but Allen reiterated the point he'd made months ago: you can't hoist and initialize the class declaration as you can a function declaration. Rather, class decl is like const decl. That was my point.

If "hoist" means only the binding, with a TDZ protecting use before in-evaluation-order init, then we agree.

If hoist means (as I used it, and even the words from Allen you quote do, viz "hoisting class definitions and immediately initializing them the way we do with functions just doesn't work...") moving the initiialization up too, then that misorders expression evaluation.

I'm pretty happy with the thread, and with Allen's strawman based on your suggestion and Dave's minimal classes work.

# Russell Leggett (9 years ago)

On Fri, Mar 23, 2012 at 1:38 AM, Brendan Eich <brendan at mozilla.org> wrote:

Russell Leggett wrote:

This is what Allen said about hoisting for this spec (Its been a long thread, not sure if you missed this.):

Thanks, I did catch up that far on the thread, but Allen reiterated the point he'd made months ago: you can't hoist and initialize the class declaration as you can a function declaration. Rather, class decl is like const decl. That was my point.

If "hoist" means only the binding, with a TDZ protecting use before in-evaluation-order init, then we agree.

If hoist means (as I used it, and even the words from Allen you quote do, viz "hoisting class definitions and immediately initializing them the way we do with functions just doesn't work...") moving the initiialization up too, then that misorders expression evaluation.

Yes, hoisting just the binding is really the only way to avoid stalling out. I think it is easy to reason about, works as desugaring, and hits the 90% case.

I'm pretty happy with the thread, and with Allen's strawman based on your suggestion and Dave's minimal classes work. Thanks!

No problem. I keep hearing you say you have doubts about classes making it, and it is easy to see why - and yet I think everyone would like at least this most minimal classes.

"The quest stands upon the edge of a knife. Stray but a little and it will fail. But hope remains, if friends stay true."

I have hope :)

# Claus Reinke (9 years ago)

Thanks, I did catch up that far on the thread, but Allen reiterated the point he'd made months ago: you can't hoist and initialize the class declaration as you can a function declaration. Rather, class decl is like const decl. That was my point.

If "hoist" means only the binding, with a TDZ protecting use before in-evaluation-order init, then we agree.

I'm probably missing something, but since the current proposal is meant to be sugar around the old constructor/prototype pattern:

  • would it make sense to name the constructor after the class (avoiding 'constructor' and 'new')?
  • could we think of hoisting a class as hoisting its constructor (the hoisted binding wouldn't be undefined, but shouldn't
    be called before initialization, either)?

Claus

# Gavin Barraclough (9 years ago)

On Mar 23, 2012, at 12:42 AM, Claus Reinke wrote:

  • would it make sense to name the constructor after the class (avoiding 'constructor' and 'new')?

I was about to suggest exactly the same thing.

If I see a line of code saying let x = new FooBar(); and want to understand what it does, I'm instinctively looking for a function named FooBar, not one named either 'new' or 'constructor'.

Between 'new' and 'constructor', new seems like the better choice to me. Whilst it is true that prototypes currently contain a constructor property, I don't see a lot of code that explicitly accesses this. In terms of the language actually actively in use in scripts by ES programmers, 'new' is currently in the common lexicon, and 'constructor' doesn't seem to be. [ I make this statement on casual observation rather than any empirical evidence. :-) ]

# Alex Russell (9 years ago)

On Mar 21, 2012, at 1:42 AM, Erik Arvidsson wrote:

On Mon, Mar 19, 2012 at 13:03, Russell Leggett <russell.leggett at gmail.com> wrote:

So what do you say people? Is it safe enough?

Yes.

One of the biggest arguments I’ve heard against rushing in a class syntax now is that once its in we have to keep supporting it.

This argument might have worked a year or two ago when we handn't all been around the entire solution space multiple times...but we have. We know now that most of the options involve some tradeoff that is, in some way, distasteful...including leaning on literal syntaxes when what you really want is a class. So we need to disallow this argument unless it's backed up with a specific future-compat critique.

I say that this is small enough we won’t regret it, and makes it possible to do a lot more in the future. If something more substantial can be agreed on soon enough to make it into ES6, even better, but maybe we can at least have a backup plan.

+1

Seconded. We've already walked back a loooong way from the proposals that Bob, Peter, Arv, and I made a year+ ago, and traits as the composition mechanism seem very far off. But nothing gets off the ground without classes. We need this, and we need it stat.

# Alex Russell (9 years ago)

Sorry I'm late to this party.

On Mar 20, 2012, at 6:22 PM, Russell Leggett wrote:

On Tue, Mar 20, 2012 at 2:09 PM, Rick Waldron <waldron.rick at gmail.com> wrote: [ ... snip ] class Animal {

constructor(name){

    this.name = name;

}

move(meters){

    alert(this.name + " moved " + meters + "m.");

}

}

Russ,

I'm trying to write up some supporting examples, but ran into a snag regarding "static" properties. eg

function Foo( stuff ) { this.stuff = stuff || ""; }

Foo.prototype.getStuff = function() { return this.stuff; };

Foo.Bar = function() { return new Foo("bar"); };

How would I make Foo.Bar ?

In my opinion, because it is a point of contention, there should be no special support for statics.

I agree. Modules also help to make them more-or-less moot.

# Andreas Rossberg (9 years ago)

On 23 March 2012 08:42, Claus Reinke <claus.reinke at talk21.com> wrote:

  • would it make sense to name the constructor after the class (avoiding 'constructor' and 'new')?

-1.

I always considered this a bad choice of C++-derived class systems. It violates Don't-repeat-yourself, and thus is annoying e.g. when you rename things. It is less searchable and less readable locally, because you have to take the name of the surrounding class into account to decide what kind of declaration you are looking at. And it doesn't scale to anonymous classes, in case these ever become an option for ES.

# Herby Vojčík (9 years ago)

Andreas Rossberg wrote:

On 23 March 2012 08:42, Claus Reinke <claus.reinke at talk21.com <mailto:claus.reinke at talk21.com>> wrote:

- would it make sense to name the constructor after the class
   (avoiding 'constructor' and 'new')?

-1.

I always considered this a bad choice of C++-derived class systems. It violates Don't-repeat-yourself, and thus is annoying e.g. when you rename things. It is less searchable and less readable locally, because you have to take the name of the surrounding class into account to decide what kind of declaration you are looking at. And it doesn't scale to anonymous classes, in case these ever become an option for ES.

Good point. ;-)

The 'constructor' gets created anyway if classes are to be compatible with plain constructor functions semantics. That is big plus for 'constructor' (and big minus for 'new' since people may look for Class.prototype["new"} and not find it).

I will repeat myself, I proposed this thing but no one reacted. It's short, does not clash as new mentioned before, and does not preclude constuctor magic in any way:

class Class extends SuperClass { @(params) { body } // constructor @x(params) { body } // private-named method y(params) { body } // plain method }

Of course, this syntax is based on '@xxx' private name access pattern is also in the language - constructor is intentionally reusing this syntax, but with 'empty identifier' - as a special short construct to use for constructor. Reusing private name syntax is safe as to name clash, because it does not clash with property names (and using empty identifier, neither does clash with real private names).

# Russell Leggett (9 years ago)

On Fri, Mar 23, 2012 at 7:00 AM, Herby Vojčík <herby at mailbox.sk> wrote:

Andreas Rossberg wrote:

On 23 March 2012 08:42, Claus Reinke <claus.reinke at talk21.com <mailto:claus.reinke at talk21.**com <claus.reinke at talk21.com>>> wrote:

  • would it make sense to name the constructor after the class (avoiding 'constructor' and 'new')?

-1.

I always considered this a bad choice of C++-derived class systems. It violates Don't-repeat-yourself, and thus is annoying e.g. when you rename things. It is less searchable and less readable locally, because you have to take the name of the surrounding class into account to decide what kind of declaration you are looking at. And it doesn't scale to anonymous classes, in case these ever become an option for ES.

Good point. ;-)

The 'constructor' gets created anyway if classes are to be compatible with plain constructor functions semantics. That is big plus for 'constructor' (and big minus for 'new' since people may look for Class.prototype["new"} and not find it).

I will repeat myself, I proposed this thing but no one reacted. It's short, does not clash as new mentioned before, and does not preclude constuctor magic in any way:

class Class extends SuperClass { @(params) { body } // constructor @x(params) { body } // private-named method y(params) { body } // plain method }

Of course, this syntax is based on '@xxx' private name access pattern is also in the language - constructor is intentionally reusing this syntax, but with 'empty identifier' - as a special short construct to use for constructor. Reusing private name syntax is safe as to name clash, because it does not clash with property names (and using empty identifier, neither does clash with real private names).

I'm not sure if you're suggesting we add private name support for this proposal, but that would be feature creep. If you are suggesting only @() for constructor, then I think that would still be pretty confusing and grawlixy. I suppose you could make the argument that the underlying constructor itself is some private property, and therefore makes sense to use @, but that seems like exposing internals on something that should be straightforward. If that was not your intention, then I think it's even more confusing because you're associating the constructor with something private.

# Herby Vojčík (9 years ago)

Russell Leggett wrote:

On Fri, Mar 23, 2012 at 7:00 AM, Herby Vojčík <herby at mailbox.sk <mailto:herby at mailbox.sk>> wrote: I will repeat myself, I proposed this thing but no one reacted. It's short, does not clash as new mentioned before, and does not preclude constuctor magic in any way:

class Class extends SuperClass {
  @(params) { body } // constructor
  @x(params) { body } // private-named method
  y(params) { body } // plain method
}

Of course, this syntax is based on '@xxx' private name access
pattern is also in the language - constructor is intentionally
reusing this syntax, but with 'empty identifier' - as a special
short construct to use for constructor. Reusing private name syntax
is safe as to name clash, because it does not clash with property
names (and using empty identifier, neither does clash with real
private names).

I'm not sure if you're suggesting we add private name support for this proposal, but that would be feature creep. If you are suggesting only

No. I am just aware of possibility that foo. at bar may end up as syntax for foo[bar] if bar is private name, freeing [] for overloading with "collection element access" as in object reformation proposal.

In this proposal, I am silently assuming it is so (the @x is from there).

@() for constructor, then I think that would still be pretty confusing and grawlixy. I suppose you could make the argument that the underlying constructor itself is some private property, and therefore makes sense

Exactly. With some made emphasized.

to use @, but that seems like exposing internals on something that should be straightforward. If that was not your intention, then I think

Or, more "special" then "private".

it's even more confusing because you're associating the constructor with something private.

As I noted above, constructor is something 'special' or 'magical' anyway. It is true that if string name should be use, constructor is most appropriate because, well, it is there already.

If other name should be used, it creates confusion that it is treated specially, so the mathod of that name is not present, and on the contrary, it is present there under the name 'constructor' (!!!).

So if people want something new, possibly not a reserved name because of previous paragraph, the idea is to reuse private syntax but with 'null identifier'.

It is special (which constructor is), and short. Yes, grawlixy, but not conpletely nonsensical, because @xxx is (in this parallel universe where foo. at bar is accepted) used for naming other special things (private properties) anyway.

# Allen Wirfs-Brock (9 years ago)

On Mar 23, 2012, at 2:48 AM, Andreas Rossberg wrote:

On 23 March 2012 08:42, Claus Reinke <claus.reinke at talk21.com> wrote:

  • would it make sense to name the constructor after the class (avoiding 'constructor' and 'new')?

-1.

I always considered this a bad choice of C++-derived class systems. It violates Don't-repeat-yourself, and thus is annoying e.g. when you rename things. It is less searchable and less readable locally, because you have to take the name of the surrounding class into account to decide what kind of declaration you are looking at. And it doesn't scale to anonymous classes, in case these ever become an option for ES

Not to mention that there is no particular reason than instance method can't have the same name as its class:

class Point {} { constructor(x,y) { thls.x = x; this.y = y; } Point () { return this } }

# Allen Wirfs-Brock (9 years ago)

On Mar 23, 2012, at 12:42 AM, Claus Reinke wrote:

  • could we think of hoisting a class as hoisting its constructor (the hoisted binding wouldn't be undefined, but shouldn't be called before initialization, either)?

But its value could be accessed? It's properties could be accessed?

This would be a new kind of Temporal deal zone. I don't think we need it. Can you show a use case were the current proposal (class X{} scopes just like const X;) doesn't work.

# Russell Leggett (9 years ago)

On Fri, Mar 23, 2012 at 12:14 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

On Mar 23, 2012, at 12:42 AM, Claus Reinke wrote:

  • could we think of hoisting a class as hoisting its constructor (the hoisted binding wouldn't be undefined, but shouldn't be called before initialization, either)?

But its value could be accessed? It's properties could be accessed?

This would be a new kind of Temporal deal zone. I don't think we need it. Can you show a use case were the current proposal (class X{} scopes just like const X;) doesn't work.

Yeah, this would be a pretty awkward middle phase - you could say new X(), but not use any methods. OTOH, one possible use case would be the ability to do inheritance out of source order.

//can still extend A because the resulting constructor is hoisted
class B extends A{...}

class A{...}

The only weird thing is wouldn't that mean you could somehow do a circular inheritance tree?

class B extends A{...}

class A extends B{...}

That should probably not be legal but begs the question how does extension fit into hoisting. Would this work?

class C extends B{...}
class B extends A{...}
class A{...}

Desugaring extension into <| would force it back into an assignment expression again instead of a function declaration, so following that logic, only A would be hoisted in my last example, and so class C would fail.

I think we should stick with const style hoisting. The restrictions are not very difficult and the result is cleaner.

# Allen Wirfs-Brock (9 years ago)

On Mar 23, 2012, at 9:48 AM, Russell Leggett wrote:

... That should probably not be legal but begs the question how does extension fit into hoisting. Would this work?

class C extends B{...}
class B extends A{...}
class A{...}

Desugaring extension into <| would force it back into an assignment expression again instead of a function declaration, so following that logic, only A would be hoisted in my last example, and so class C would fail.

A wouldn't initialize early either. If class C extends B is treated as const C=B <| function() {} then for consisteny we must treat class A{}; as const A=function() {}

I think we should stick with const style hoisting. The restrictions are not very difficult and the result is cleaner.

exactly

# David Herman (9 years ago)

On Mar 23, 2012, at 9:14 AM, Allen Wirfs-Brock wrote:

This would be a new kind of Temporal deal zone. I don't think we need it.

Agreed.

# Brendan Eich (9 years ago)

Russell Leggett wrote:

No problem. I keep hearing you say you have doubts about classes making it, and it is easy to see why - and yet I think everyone would like at least this most minimal classes.

"The quest stands upon the edge of a knife. Stray but a little and it will fail. But hope remains, if friends stay true."

I have hope :)

Good quote.

We need to overcome at least one TC39er who values use-before-init for instance properties, perhaps only if const or guarded, so (I hope) we can defer those without being hostile to them in the future.

# Brendan Eich (9 years ago)

Yet didn't we go over this previously?

esdiscuss/2011-June/015014 (from you ;-)

in reply to

esdiscuss/2011-June/015012 (from Bob Nystrom)

It's also a bit novel to preempt the class name in its own prototype, but usually ok. Any of {constructor, new, <classname>} is a special form

as noted. It's true that 'constructor' avoids collision but OTOH, it's overlong. None of these names avoids magic wiring behind the scene.

Someone on twitter pointed out that class name minifies well, but so does constructor, and anyway transport-encoding compression makes this moot.

Anonymous classes are an important use case IMHO, to match anonymous functions and the prototypal pattern.

# Gavin Barraclough (9 years ago)

Yeah, I forgot about anonymous classes again. :-)

Sadly (as a fan of using the classname) I so think that's a good argument.

G.

# Brendan Eich (9 years ago)

Brendan Eich wrote:

We need to overcome at least one TC39er who values "requiring an error for" use-before-init for instance properties, perhaps only if const or guarded, so (I hope) we can defer those without being hostile to them in the future.

Hope it was clear the first time...

# Russell Leggett (9 years ago)

On Fri, Mar 23, 2012 at 2:06 PM, Brendan Eich <brendan at mozilla.org> wrote:

Brendan Eich wrote:

We need to overcome at least one TC39er who values

"requiring an error for"

use-before-init for instance properties, perhaps only if const or

guarded, so (I hope) we can defer those without being hostile to them in the future.

Hope it was clear the first time...

I'll admit I was a little confused. Makes sense now. To be clear, you would be referring to:

let foo = new Foo();
foo.fail(); //this should be an error

class Foo {
    fail(){
        alert("This alert won't happen.");
    }

    success(){
        alert("This should be fine");
    }
}

let foo2 = new Foo();
foo2.success(); //would work fine

and by error you mean before the code is actually evaluated?

# Allen Wirfs-Brock (9 years ago)

On Mar 23, 2012, at 11:33 AM, Russell Leggett wrote:

On Fri, Mar 23, 2012 at 2:06 PM, Brendan Eich <brendan at mozilla.org> wrote: Brendan Eich wrote: We need to overcome at least one TC39er who values "requiring an error for"

use-before-init for instance properties, perhaps only if const or guarded, so (I hope) we can defer those without being hostile to them in the future. Hope it was clear the first time...

I think that the current "maximal minimal" proposal is quite future friendly WRT any future hoisting variants . Presumably any new alternative hoisting semantics for class declarations would only be observable in that some things that had been a runtime (or possible early) error under the current proposal might no longer be errors. Defining a new non-error semantics for what was perviously an error condition is usually a safe change.

I'll admit I was a little confused. Makes sense now. To be clear, you would be referring to:

let foo = new Foo();

under the current proposal the above is also an error, because Foo is not yet initialized. You would never get to the next statment.

# Russell Leggett (9 years ago)

On Fri, Mar 23, 2012 at 3:40 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

On Mar 23, 2012, at 11:33 AM, Russell Leggett wrote:

On Fri, Mar 23, 2012 at 2:06 PM, Brendan Eich <brendan at mozilla.org> wrote:

Brendan Eich wrote:

We need to overcome at least one TC39er who values

"requiring an error for"

use-before-init for instance properties, perhaps only if const or

guarded, so (I hope) we can defer those without being hostile to them in the future.

Hope it was clear the first time...

I think that the current "maximal minimal" proposal is quite future friendly WRT any future hoisting variants . Presumably any new alternative hoisting semantics for class declarations would only be observable in that some things that had been a runtime (or possible early) error under the current proposal might no longer be errors. Defining a new non-error semantics for what was perviously an error condition is usually a safe change.

I'll admit I was a little confused. Makes sense now. To be clear, you would be referring to:

let foo = new Foo();

under the current proposal the above is also an error, because Foo is not yet initialized. You would never get to the next statment.

Yeah, that's one of the things that confused me. Brendan specified instance properties, which made me think he was referring to an error that might occur prior to actually evaluating the code.

# Brendan Eich (9 years ago)

Russell Leggett wrote:

Yeah, that's one of the things that confused me. Brendan specified instance properties, which made me think he was referring to an error that might occur prior to actually evaluating the code.

Yes, the issue is not the class binding (if the class was declared, rather than expressed). The issue is instance variables. I'm going back to the old syntax from last year that we don't like any longer, which declared i.v.s in the constructor body:

class Point extends Evil { constructor(ax, ay) { public x, y; super(); this.x = ax; this.y = ay; } ... }

The issue is what happens if class Evil looks like this:

class Evil { constructor() { console.log(this.x); } }

Should undefined by logged, or an error thrown? If the i.v.s are made const, or guarded by guard expressions, does the answer change? Can we do non-const i.v.s now and let undefined be read, and be future-friendly?

I think we can -- it seems obvious to me.

But I may be missing something (since we haven't got const i.v.s or guards in Harmony yet, who knows? This is the unfalsifiable limit where we can't do classes until we do other more advanced and uncertain things).

Also it may be that some on TC39 think the answer to "Should undefined be logged?" for the example above is "no, error thrown". I don't agree with that, it's not how writable properties on objects work in JS.

# Russell Leggett (9 years ago)

On Fri, Mar 23, 2012 at 4:17 PM, Brendan Eich <brendan at mozilla.org> wrote:

Russell Leggett wrote:

Yeah, that's one of the things that confused me. Brendan specified instance properties, which made me think he was referring to an error that might occur prior to actually evaluating the code.

Yes, the issue is not the class binding (if the class was declared, rather than expressed). The issue is instance variables. I'm going back to the old syntax from last year that we don't like any longer, which declared i.v.s in the constructor body:

class Point extends Evil { constructor(ax, ay) { public x, y; super(); this.x = ax; this.y = ay; } ... }

The issue is what happens if class Evil looks like this:

class Evil { constructor() { console.log(this.x); } }

Should undefined by logged, or an error thrown? If the i.v.s are made const, or guarded by guard expressions, does the answer change? Can we do non-const i.v.s now and let undefined be read, and be future-friendly?

I think we can -- it seems obvious to me.

But I may be missing something (since we haven't got const i.v.s or guards in Harmony yet, who knows? This is the unfalsifiable limit where we can't do classes until we do other more advanced and uncertain things).

Also it may be that some on TC39 think the answer to "Should undefined be logged?" for the example above is "no, error thrown". I don't agree with that, it's not how writable properties on objects work in JS.

Ah, makes more sense now. I think there's a lot of room for future work. The current behavior that I think we're agreeing on seems very intuitive with the way JS works now. Future behavior should have more signposts like the "const" keyword would for const i.v.s.

# Claus Reinke (9 years ago)
  • would it make sense to name the constructor after the class (avoiding 'constructor' and 'new')?

That seems to be a 'no', then:-) It might be useful to document such obvious but rejected options, with counterarguments:

  • repeated name
  • readability/search concerns (constructor determined non-locally, by class name matching)
  • what to do about anonymous classes?
  • preempts class name as prototype property

The various arguments revolving about names and anonymous classes suggest another alternative:

  • use an anonymous function declaration in the class as the class constructor

Again, I'm not arguing in favor or against, I'm only looking for alternative design options.

Claus

# Claus Reinke (9 years ago)
  • could we think of hoisting a class as hoisting its constructor

This would be a new kind of Temporal deal zone. I don't think we need it. Can you show a use case were the current proposal (class X{} scopes just like const X;) doesn't work.

Thinko of mine, sorry. The reasoning was that

class X{ constructor(){} }

is short for function X(){}

and should be hoisted a such. But with the .prototype properties, it would be more like

class X{ constructor(){}; props }

is short for {props} <| function X(){}

which doesn't work (nor does hoisting the constructor without the .prototype properties), while

class X{ constructor(){}; props }

is short for const X = {props} <| function (){}

does make all-or-nothing sense.

Claus

# Brendan Eich (9 years ago)

Allen Wirfs-Brock wrote:

I'm quite certain that class-side inheritance is a good idea for dynamic class based OO languages with inheritance. It has proven utility. There may be things about that kind of language that an individual might not like. They might object to dynamic, or class-based, or OO, or inheritance. But if you have that combination, inheritable class methods make a lot of sense.

+1

In particular (and I did not cite your words) I think the interpretation of class methods as "static" is a stumbling block, and using |this| more, to increase genericity, is good.

The ultimate generic prorgramming model might be purely functional, but |this| is "just" another parameter. We shouldn't bend JS to fit on the static Procrustean bed of Java or C#.

# David Herman (9 years ago)

On Mar 23, 2012, at 1:17 PM, Brendan Eich wrote:

class Point extends Evil { constructor(ax, ay) { public x, y; super(); this.x = ax; this.y = ay; } ... }

class Evil { constructor() { console.log(this.x); } }

Should undefined by logged, or an error thrown? ...

I think we can -- it seems obvious to me.

... "no, error thrown". I don't agree with that, it's not how writable properties on objects work in JS.

+1

That kind of protection matters if you're trying to define a statically typed class system that can guarantee all properties are initialized before use, e.g., to have non-nullable types. That's not what we're doing here. We are doing classes as a codification of existing practice. So you can refer to properties before they've been initialized, and you get undefined.

# David Herman (9 years ago)

On Mar 21, 2012, at 9:13 AM, Allen Wirfs-Brock wrote:

On Mar 20, 2012, at 11:32 PM, David Herman wrote:

Well, hang on now. The 'constructor' syntax is not "just the constructor property." It still carries special status; the semantics of a class says "look for the property called 'constructor' and make that the [[Call]] and [[Construct]] behavior of the class."

Actually, the semantics is probably more like: the value bound to the <classMame> is the function object defined by the <methodDefinition> whose <propertyName> is "constructor".

Yes, sure. Doesn't change the point: 'constructor' is still a special, distinguished method.

Regardless of whether we spell it 'constructor' or 'new' it requires special semantics that says "there's one distinguished method form in the body that determines the constructor of the class."

There is one distinguished method that is the class object (aka, the constructor function). A class declaration essentially desugars into the definition of a constructor function and a object that is the prototype associated with that constructor.

Again, doesn't change the point: whether you spell it 'new' or 'constructor' it's still a distinguished method, and then you can desugar that however you want.

The question is how we spell that. This is 99.9% a surface syntax question. Tou could argue that spelling it 'new' should define a ["new"] method, or a ["new"] method and a ["constructor"] method, or just a ["constructor"] method. If the latter, it's semantically identical to spelling it 'constructor'. But even if we chose one of the other two alternatives, the semantic differences here are minor, and the ergonomics of the syntax matter.

You need to drop the [ ]'s (although I'm not sure what you meant by them...

I meant that there is a property of the prototype that you can access via either p["new"] or p["constructor"] or both, depending on which semantics we decide to give. No matter what the surface syntax, any of those semantics is available to us. I say:

a) spell it "new" -- ergonomics trumps corner cases; hard cases make bad law

b) desugar it to the constructor function and the p.constructor property only

c) i.e., don't create a p.new property -- no more prototype pollution please

d) an explicit 'constructor' method overrides the implicit creation of the 'constructor' method but does not define the constructor function

Why d)? Remember, the .constructor idiom is a very weak idiom that many JS programs don't follow. If a JS program has some reason to use 'constructor' for a different purpose, trust them.

Personally I think the answer should be "A" which implies that we have class-side inheritance. This is a departure from current practice but because "classes are functions" there is no way in ES<=5.1 to set up class side inheritance other than by mutating proto.

I always found this the more appealing, but then again, if I'm supposed to be going with the opposite of my instincts (see above), then maybe I should disagree with you. ;)

I would guess that your instinctive response comes from thinking about a "class" is something more than just a composite of objects. We can talk more about this later after I respond to Mark

I think you misread me. My instinctive response agrees with yours, not Mark's.

If the value of SOMEEXPRESSION is a constructor function (typeof == "function" && and has a "prototype" property) then the new constructor inherits from SOMEEXPRESSION and the new prototype inherits from SOMEEXPRESSION.prototype. Otherwise, the new consructor inherits from Function.prototype and the new prototype inherits from SOMEEXPRESSION. That is essentially the semantics I've defined for SOMEEXPRESSION <| function () {}

I'm not happy with that semantics, for either classes or <| (I believe others have objected on the list to the special-case semantics for <| as well). Since functions are objects, you can pass functions into contexts that expect an object and those contexts don't need to care whether the object they have is a function or an object. So this will lead to WTFjs moments where people take an object they got passed in from someone else and create a class with it, and it won't be wired up right because they didn't realize the object was a function.

This kind of special-case ad hoc type testing in the semantics has a bad smell. It reminds me of stuff like the Array constructor that special-cases the number argument.

B = do{ let B = SOMEEXPRESSION <| function B(...) { ...}; B.prototype= SOMEEXPRESSION.prototype <| {constructor: B, ...} B }

(or to be maximally explicit}

B= do { let B = function B(...) {...}; if (typeof SOMEEXPRESSION == "function" && typeof SOMEEXPRESSION.prototype == "object") B.proto=SOMEEXPRESSION; B.prototype= SOMEEXPRESSION.prototype <| {constructor: B, ...} B }

Nit: you'd need to bind the result of SOMEEXPRESSION to a temporary, to avoid duplicating side effects. </macrology nerd>

# Nadav Shesek (9 years ago)

What about setting arbitrary expressions as the value for prototype methods? Being able to use higher-order functions to dynamically create functions is very important for functional style programming. I use it very often to decorate functions, compose functions together, set partially applied functions as methods, etc. It seems to be impossible with syntax proposed here - I think adding it to the "safety syntax" is very much needed and should not be over looked.

# David Herman (9 years ago)

On Mar 24, 2012, at 3:01 PM, Nadav Shesek wrote:

What about setting arbitrary expressions as the value for prototype methods? Being able to use higher-order functions to dynamically create functions is very important for functional style programming. I use it very often to decorate functions, compose functions together, set partially applied functions as methods, etc. It seems to be impossible with syntax proposed here - I think adding it to the "safety syntax" is very much needed and should not be over looked.

Yep, we already agreed to this -- see the grammar on Allen's "maximally minimal" proposal:

http://wiki.ecmascript.org/doku.php?id=strawman:maximally_minimal_classes#class_declarations_and_expressions
# Allen Wirfs-Brock (9 years ago)

On Mar 24, 2012, at 7:28 AM, David Herman wrote:

On Mar 21, 2012, at 9:13 AM, Allen Wirfs-Brock wrote:

On Mar 20, 2012, at 11:32 PM, David Herman wrote:

Well, hang on now. The 'constructor' syntax is not "just the constructor property." It still carries special status; the semantics of a class says "look for the property called 'constructor' and make that the [[Call]] and [[Construct]] behavior of the class."

Actually, the semantics is probably more like: the value bound to the <classMame> is the function object defined by the <methodDefinition> whose <propertyName> is "constructor".

Yes, sure. Doesn't change the point: 'constructor' is still a special, distinguished method.

Yes it is. I think the core issue WRT to identifying this distinguished method with an identifier other than "constructor" concerns how it would relate to an actual method named "constructor".

The conservative course is to use "constructor" as the distinguishing identifier. It is a direct reflection of the underlying ES<=5.1 chapter 13 and15 "class" model and it doesn't introduce any of these name naming issues.

I primarily favor sticking with "constructor" because the safety/maximally-miminal proposal is all about being conservative...

...

The question is how we spell that. This is 99.9% a surface syntax question. Tou could argue that spelling it 'new' should define a ["new"] method, or a ["new"] method and a ["constructor"] method, or just a ["constructor"] method. If the latter, it's semantically identical to spelling it 'constructor'. But even if we chose one of the other two alternatives, the semantic differences here are minor, and the ergonomics of the syntax matter.

You need to drop the [ ]'s (although I'm not sure what you meant by them...

I meant that there is a property of the prototype that you can access via either p["new"] or p["constructor"] or both, depending on which semantics we decide to give. No matter what the surface syntax, any of those semantics is available to us. I say:

a) spell it "new" -- ergonomics trumps corner cases; hard cases make bad law

deviates from chapters 13 and 15.

(class () {}) isn't interchangeable with (function () {}). Shouldn't it be?

b) desugar it to the constructor function and the p.constructor property only

but presumably means that an instance method named new can't be defined using a class definition. Or, perhaps only if "new" is explicitly string quoted as a property name. "new" is not a totally unreasonable method name. Also presumably means that defining an instance method named constructor is disallowed.

c) i.e., don't create a p.new property -- no more prototype pollution please

I presume you mean it also doesn't define a p.constructor property. This has similar problems to a). It deviates from the legacy ES "class model"

Also, most instance inherit from Object.prototype so they will still have an inherited "constructor" property whose value is Object.

d) an explicit 'constructor' method overrides the implicit creation of the 'constructor' method but does not define the constructor function

Why d)? Remember, the .constructor idiom is a very weak idiom that many JS programs don't follow. If a JS program has some reason to use 'constructor' for a different purpose, trust them.

I believe the constructor idiom is most commonly not followed today when the prototype property of a function is set to a new object (perhaps defined using an object literal) and correctly dynamically setting the "constructor" property is an extra step that is easily forgotten (an usually as no ill-effects)

However, with syntactic class definition support (including inheritance) in the language I am sure that we are going to see much more use of OO idioms in ES programs (if that wasn't the case why would be add them). A very common idiom is to query the "class" of an object. Just look how frequently you see could like p.class in Java or Ruby code or( p class) in Smalltalk code. Thew equivalent of this using ES chapter 13/`5 objects is p.constructor. Which would you prefer to see in future ES code p.constructor===q.constructor or p.new===q.new

BTW, many OO experts including myself, tell people that querying the class of an object in this manner is an undesirable practice. Maybe even an anti-pattern. But in reality it is widely done and the negatives all relate to non-fucctional issues like code flexibility and reusability. Developer are going to do it, probably a lot.

Personally I think the answer should be "A" which implies that we have class-side inheritance. This is a departure from current practice but because "classes are functions" there is no way in ES<=5.1 to set up class side inheritance other than by mutating proto.

I always found this the more appealing, but then again, if I'm supposed to be going with the opposite of my instincts (see above), then maybe I should disagree with you. ;)

I would guess that your instinctive response comes from thinking about a "class" is something more than just a composite of objects. We can talk more about this later after I respond to Mark

I think you misread me. My instinctive response agrees with yours, not Mark's.

If the value of SOMEEXPRESSION is a constructor function (typeof == "function" && and has a "prototype" property) then the new constructor inherits from SOMEEXPRESSION and the new prototype inherits from SOMEEXPRESSION.prototype. Otherwise, the new consructor inherits from Function.prototype and the new prototype inherits from SOMEEXPRESSION. That is essentially the semantics I've defined for SOMEEXPRESSION <| function () {}

Oops, the above isn't at all a correct statement of the current semantics of SOMEEXPRESSION <| function () {}. My bad! In particular the "otherwise" clause above is not that of <|.

I think it is best if I address the inheritance rules is a separate message instead of burying it here.

# Brendan Eich (9 years ago)

David Herman wrote:

On Mar 24, 2012, at 3:01 PM, Nadav Shesek wrote:

What about setting arbitrary expressions as the value for prototype methods? Being able to use higher-order functions to dynamically create functions is very important for functional style programming. I use it very often to decorate functions, compose functions together, set partially applied functions as methods, etc. It seems to be impossible with syntax proposed here - I think adding it to the "safety syntax" is very much needed and should not be over looked.

Yep, we already agreed to this -- see the grammar on Allen's "maximally minimal" proposal:

 http://wiki.ecmascript.org/doku.php?id=strawman:maximally_minimal_classes#class_declarations_and_expressions

I don't see what I read Nadav as asking for: the ability to initialize a prototype method from an arbitrary expression. What am I missing?

Class bodies

ClassElement : PrototypePropertyDefinition ; //semicolons are allowed but have no significance

PrototypePropertyDefinition : PropertyName ( FormalParameterList? ) { FunctionBody } // method *PropertyName ( FormalParameterList? ) { FunctionBody } // generator method get PropertyName ( ) { FunctionBody } // getter set PropertyName ( ProopertySetParameterList ) { FunctionBody } // setter

# Allen Wirfs-Brock (9 years ago)

On Mar 24, 2012, at 7:29 PM, Brendan Eich wrote:

David Herman wrote:

On Mar 24, 2012, at 3:01 PM, Nadav Shesek wrote:

What about setting arbitrary expressions as the value for prototype methods? Being able to use higher-order functions to dynamically create functions is very important for functional style programming. I use it very often to decorate functions, compose functions together, set partially applied functions as methods, etc. It seems to be impossible with syntax proposed here - I think adding it to the "safety syntax" is very much needed and should not be over looked.

Yep, we already agreed to this -- see the grammar on Allen's "maximally minimal" proposal:

http://wiki.ecmascript.org/doku.php?id=strawman:maximally_minimal_classes#class_declarations_and_expressions

I don't see what I read Nadav as asking for: the ability to initialize a prototype method from an arbitrary expression. What am I missing?

I suspect Dave misinterpreted Nadav's question. So did I, when I originally read it.

The "superclass" can be set to an arbitrary AssignmentExpression. This permits using a higher-order functions to be to define the [[Prototype]] of the class' prototype object. Potentially this mechanisms might be used to essentially have the effect of injecting dynamically generated methods into the class definition. However, they would be inherited methods of the prototype object rather than own methods, although that may not matter.

To actually add a computed function as the value of a prototype object property within the class definition is pretty much the same thing as defining an arbitrary valued prototype data property. Defining non-method prototype properties is one of the features that we have previous been unable to reach consensus on and for that reason was intentionally excluded from the maximal-minimal proposal. As the proposal says:

"There is (intentionally) no direct declarative way to define either prototype data properties (other than methods) class properties, or instance property" "Class properties and prototype data properties need be created outside the declaration."

# Brendan Eich (9 years ago)

Allen Wirfs-Brock wrote:

To actually add a computed function as the value of a prototype object property within the class definition is pretty much the same thing as defining an arbitrary valued prototype data property. Defining non-method prototype properties is one of the features that we have previous been unable to reach consensus on and for that reason was intentionally excluded from the maximal-minimal proposal. As the proposal says:

  • "There is (intentionally) no direct declarative way to define either prototype data properties (other than methods) class properties, or instance property"
  • "Class properties and prototype data properties need be created outside the declaration."

Thanks, and I'm hip to this already and agree with the design. The consequence for Nadav's scenario is that the whole prototype must be computed, via extends as you note, or just manual desugaring.

It's the right trade-off, and not just to get appropriately minimal classes into ES6 -- the computed method case is often part of a generative framework anyway, so won't obviously fit into any general, declarative syntax.

At least, this is my gut reaction -- but it would be helpful to see a concrete example in the wild (not contrived) and look deeper.

# David Herman (9 years ago)

On Mar 24, 2012, at 5:54 PM, Allen Wirfs-Brock wrote:

I primarily favor sticking with "constructor" because the safety/maximally-miminal proposal is all about being conservative...

I think you'll find what I'm saying is as conservative as what you've been advocating. Your reply demonstrates that we've been talking past each other.

a) spell it "new" -- ergonomics trumps corner cases; hard cases make bad law deviates from chapters 13 and 15.

(class () {}) isn't interchangeable with (function () {}). Shouldn't it be?

When I say "spell it 'new'" I mean "the surface syntax is spelled 'new'". That has nothing to do with the semantics. The semantics is described in point b). Keep reading...

b) desugar it to the constructor function and the p.constructor property only

but presumably means that an instance method named new can't be defined using a class definition.

Not with the declarative syntax, no. But easily enough imperatively:

class C { new(x) { this.x = x } } // defines C.prototype.constructor === C
C.prototype.new = function() { console.log("I am C's 'new' method") }

Notice the comment: the class definition uses 'new' for the surface syntax, but that defines C.prototype.constructor, not C.prototype.new. If you want to add a prototype method called 'new', you can do that imperatively, the same old way as ever in JS.

Or, perhaps only if "new" is explicitly string quoted as a property name. "new" is not a totally unreasonable method name. Also presumably means that defining an instance method named constructor is disallowed.

That's true, "new" is not an unreasonable method name. It's still a rare method name. Syntax is about optimizing for common cases. With my proposed syntax, you don't have a declarative way to create a prototype method called "new" but you still have an imperative way to do it, the same old way as before.

c) i.e., don't create a p.new property -- no more prototype pollution please

I presume you mean it also doesn't define a p.constructor property. This has similar problems to a).

No, no, see b) -- it does define a p.constructor property!

What I've been trying to explain is that we can use "new" for the surface syntax but desugar it just the same as the "constructor" version: the constructor becomes both the value of the class and the p.constructor property.

It deviates from the legacy ES "class model"

It implements exactly the legacy ES class model.

Also, most instance inherit from Object.prototype so they will still have an inherited "constructor" property whose value is Object.

They will not, because p.constructor is the constructor.

A very common idiom is to query the "class" of an object. Just look how frequently you see could like p.class in Java or Ruby code or( p class) in Smalltalk code. Thew equivalent of this using ES chapter 13/`5 objects is p.constructor. Which would you prefer to see in future ES code p.constructor===q.constructor or p.new===q.new

My proposal is not, and has never been, to change the C.prototype.constructor idiom. It is to choose a different surface syntax that desugars to exactly the classic idiom.

# Axel Rauschmayer (9 years ago)

One thing that I still would add (and haven't seen mentioned in the proposal) are computed property names. Manually creating private names is not a problem, neither is using this[] to add private instance data properties. But having to add private methods externally to the class declaration would be cumbersome.

[[[Sent from a mobile device. Please forgive brevity and typos.]]]

Dr. Axel Rauschmayer axel at rauschma.de Home: rauschma.de Blog: 2ality.com

# David Herman (9 years ago)

On Mar 24, 2012, at 10:20 PM, Allen Wirfs-Brock wrote:

On Mar 24, 2012, at 7:29 PM, Brendan Eich wrote:

I suspect Dave misinterpreted Nadav's question. So did I, when I originally read it.

Oh, yes, thanks for the clarification. I thought he was talking about the superclass position, but indeed he was talking about the prototype methods. He was clear, I just didn't read correctly.

To actually add a computed function as the value of a prototype object property within the class definition is pretty much the same thing as defining an arbitrary valued prototype data property. Defining non-method prototype properties is one of the features that we have previous been unable to reach consensus on and for that reason was intentionally excluded from the maximal-minimal proposal. As the proposal says:

"There is (intentionally) no direct declarative way to define either prototype data properties (other than methods) class properties, or instance property" "Class properties and prototype data properties need be created outside the declaration."

Yeah, I think this is fine and future-proof for expanding on in the future. IOW, we're not against what Nadav's asking for, we're just trying to define the base level foundation for building on in the future.

# David Herman (9 years ago)

On Mar 24, 2012, at 10:54 PM, Brendan Eich wrote:

It's the right trade-off, and not just to get appropriately minimal classes into ES6 -- the computed method case is often part of a generative framework anyway, so won't obviously fit into any general, declarative syntax.

Since we've gone with the decision not to hoist the initialization of class declarations, I think it makes it easier to conceive of allowing computation in the initialization of a class. Nadav's request is reasonable. Again, I'm fine with deferring it, but it's also fine to discuss it, if only for thinking a step or two down the road.

At least, this is my gut reaction -- but it would be helpful to see a concrete example in the wild (not contrived) and look deeper.

One simple example that comes to mind: reusing a hand-picked set of generic methods from e.g. Array.prototype or String.prototype:

class Vector {
    /* various methods... */

    forEach = Array.prototype.forEach; // just an example, don't bikeshed me

    /* more methods ... */
}

Then again, this example is also in the space of what traits would address, which I think are worth revisiting post-ES6. You could imagine a trait definition like:

trait Sequence {
    forEach: Array.prototype.forEach,
    /* etc... */
}

class Vector with Sequence {
    /* various methods... */
}

I could imagine keeping the declarative syntax of classes pretty spare by beefing up the expressiveness of traits. But this requires a lot more design work. Again, this is all outside the scope of ES6. Maximal minimalism FTW!

# Kevin Smith (9 years ago)

Regarding Nadav's point, here's a real world use case:

khs4473/JiaDOM/blob/master/src/DOMSet.js#L28-44

The function "_manip" returns a function that is parameterized by the input string.

khs4473/JiaDOM/blob/master/src/DOMSet.js#L387-403

In the "class" definition, the "_manip" function is used to concisely generate a set of methods.

# Brendan Eich (9 years ago)

David Herman wrote:

 class Vector {
     /* various methods... */

     forEach = Array.prototype.forEach; // just an example, don't bikeshed me

     /* more methods ... */
 }

Perhaps we are overreacting to the footgun of the proto-shared mutable object defined via a data property. Of course one can shoot at toes imperatively after the class declaration. And functions are mutable objects, so methods are a special case we bless even in the most minimal classes proposals!

Still, I prefer to defer anything like the above (and of course, defer the better traits thinking you showed).

# David Herman (9 years ago)

On Mar 25, 2012, at 10:30 AM, Brendan Eich wrote:

Still, I prefer to defer anything like the above (and of course, defer the better traits thinking you showed).

Yep, definitely.

# Allen Wirfs-Brock (9 years ago)

On Mar 25, 2012, at 10:30 AM, Brendan Eich wrote:

David Herman wrote:

class Vector {
    /* various methods... */

    forEach = Array.prototype.forEach; // just an example, don't bikeshed me

    /* more methods ... */
}

Perhaps we are overreacting to the footgun of the proto-shared mutable object defined via a data property. Of course one can shoot at toes imperatively after the class declaration. And functions are mutable objects, so methods are a special case we bless even in the most minimal classes proposals!

Still, I prefer to defer anything like the above (and of course, defer the better traits thinking you showed).

I'd say it is essential to progress that we defer such items. Right now we need to focus on agreeing that a maximally minimal class declaration that only includes methods is good enough for ES6. Once we accomplish that, we can get back to future embellishments. It is even possible that some embellishment might even be possible for ES6 but we can't get there if we don't first agree of maximally minimal part.

# Allen Wirfs-Brock (9 years ago)

On Mar 24, 2012, at 11:45 PM, David Herman wrote:

On Mar 24, 2012, at 5:54 PM, Allen Wirfs-Brock wrote:

...

a) spell it "new" -- ergonomics trumps corner cases; hard cases make bad law deviates from chapters 13 and 15.

(class () {}) isn't interchangeable with (function () {}). Shouldn't it be?

When I say "spell it 'new'" I mean "the surface syntax is spelled 'new'". That has nothing to do with the semantics. The semantics is described in point b). Keep reading...

Ah, I was interpreting your a-d as 4 alternative semantics rather than as a sequence defining a single semantics.

b) desugar it to the constructor function and the p.constructor property only

but presumably means that an instance method named new can't be defined using a class definition.

Not with the declarative syntax, no. But easily enough imperatively:

class C { new(x) { this.x = x } } // defines C.prototype.constructor === C C.prototype.new = function() { console.log("I am C's 'new' method") }

or class C{ new() {this.x=x} "new"(){console.logn("I am C's 'new; method")} }

(not to say that I particularly like the above, but it would work according to the current strawman.)

...

My proposal is not, and has never been, to change the C.prototype.constructor idiom. It is to choose a different surface syntax that desugars to exactly the classic idiom.

Got it.

I'm still not necessarily convinced that giving new that syntactic meaning is the best thing to do. The ES classabstraction is going to be a quite leaky abstraction. Anyone will be able to easily observe that a class object is really a constructor function with a prototype property, etc. and this knowledge is probably going to needed even by fairly unsophisticated developer. For example it is needed to understand the basic kind of class testings (a.constructor === b.constructor) that I mentioned. My suspicion is that the name difference between the syntactic sugar and the underly but highly visible implementation will cause some confusion. It also adds complexity because it requires addition static semantic rules regarding the use of new and constructor as method names within class declarations.

Finally, regarding:

d) an explicit 'constructor' method overrides the implicit creation of the 'constructor' method but does not define the constructor function

Why d)? Remember, the .constructor idiom is a very weak idiom that many JS programs don't follow. If a JS program has some reason to use 'constructor' for a different purpose, trust them.

I believe the constructor idiom is most commonly not followed today when the prototype property of a function is set to a new object (perhaps defined using an object literal) and correctly dynamically setting the "constructor" property is an extra step that is easily forgotten (an usually as no ill-effects)

I really don't like d) very much at all. I think one of the advantages that use of class declaration can offer is a higher integrity implementation of the latent ES class model. In particular, it could more strongly enforce the <ctor>.prototype === <ctor>.prototype.constructor invariant. I don't think a class declaration should be allowed to set the prototype's constructor property to anything other than the actual class object (ie constructor function). It also should make <ctor>.prototype writable:false and configurable: false, just like is done for all of the chapter 15 constructors. In addition, I suggest that we should go one step further and also make <ctor>.prototype.constructor writable:false and configurable: false. It isn't clear to me why ES1 chapter 15 made all <ctor>.prototype ReadOnly/DontDelete but didn't do the same for chapter 15 <ctor>.prototype.constructor properties.)

# Russell Leggett (9 years ago)

Finally, regarding:

d) an explicit 'constructor' method overrides the implicit creation of the 'constructor' method but does not define the constructor function

Why d)? Remember, the .constructor idiom is a very weak idiom that many JS programs don't follow. If a JS program has some reason to use 'constructor' for a different purpose, trust them.

I believe the constructor idiom is most commonly not followed today when the prototype property of a function is set to a new object (perhaps defined using an object literal) and correctly dynamically setting the "constructor" property is an extra step that is easily forgotten (an usually as no ill-effects)

I really don't like d) very much at all. I think one of the advantages that use of class declaration can offer is a higher integrity implementation of the latent ES class model. In particular, it could more strongly enforce the <ctor>.prototype === <ctor>.prototype.constructor invariant. I don't think a class declaration should be allowed to set the prototype's constructor property to anything other than the actual class object (ie constructor function). It also should make <ctor>.prototype writable:false and configurable: false, just like is done for all of the chapter 15 constructors. In addition, I suggest that we should go one step further and also make <ctor>.prototype.constructor writable:false and configurable: false. It isn't clear to me why ES1 chapter 15 made all <ctor>.prototype ReadOnly/DontDelete but didn't do the same for chapter 15 <ctor>.prototype.constructor properties.)Allen

+1

This is actually one of the reasons I still come down on constructor over new - I'd really like to discourage screwing around with <ctor>.prototype.constructor. That always felt like a major wart IMO.

Making it ReadOnly/DontDelete would be icing on the cake. Not sure if we can squeeze it into max/min, but I like it. Classes are a leaky abstraction, as you said, but I think the major gain is making it easy to do things the right way, even if we don't really add much functionality.

# Brendan Eich (9 years ago)

Russell Leggett wrote:

Finally, regarding:

d) an explicit 'constructor' method overrides the implicit creation of the 'constructor' method but does not define the constructor function

Why d)? Remember, the .constructor idiom is a very weak idiom that many JS programs don't follow. If a JS program has some reason to use 'constructor' for a different purpose, trust them.

I believe the constructor idiom is most commonly not followed today when the prototype property of a function is set to a new object (perhaps defined using an object literal) and correctly dynamically setting the "constructor" property is an extra step that is easily forgotten (an usually as no ill-effects)

I really don't like d) very much at all.  I think one of the
advantages that use of class declaration can offer  is a higher
integrity implementation of the latent ES class model.   In
particular, it could more strongly enforce the <ctor>.prototype
=== <ctor>.prototype.constructor

[Allen: extra .prototype on the left of ===, right?]

invariant.  I don't think a class declaration should be allowed to
set the prototype's constructor property to anything other than
the actual class object (ie constructor function). It also should
make <ctor>.prototype writable:false and configurable: false, just
like is done for all of the chapter 15 constructors. In addition,
I suggest that we should go one step further and also make

<ctor>.prototype.constructor writable:false and configurable: false. It isn't clear to me why ES1 chapter 15 made all <ctor>.prototype ReadOnly/DontDelete but didn't do the same for chapter 15 <ctor>.prototype.constructor properties.)

ES1 did that because implementation(s) possibly not including my "Mocha" in Netscape 2 but probably including IE3-4 JScript did that, because (if I can recall correctly) the built-ins constructors' .prototype property was used to find "the original value of Object.prototype" or "the original value of Array.prototype" given an internal memo that strongly referenced Object or Array, e.g. Without this, you'd need two internal memos, one for the ctor and one for its original .prototype value.

Kind of a dopey reason, but there it is.

+1

This is actually one of the reasons I still come down on constructor over new - I'd really like to discourage screwing around with <ctor>.prototype.constructor. That always felt like a major wart IMO. Making it ReadOnly/DontDelete would be icing on the cake. Not sure if we can squeeze it into max/min, but I like it. Classes are a leaky abstraction, as you said, but I think the major gain is making it easy to do things the right way, even if we don't really add much functionality.

The issue is "what is the right way?" I agree that classes shouldn't be too little veneer on oonstructor functions, though. Locking down the class constructor's .prototype is ok with me. It helps us move toward self-hosting the built-ins.

# David Herman (9 years ago)

On Mar 26, 2012, at 10:32 PM, Brendan Eich wrote:

This is actually one of the reasons I still come down on constructor over new - I'd really like to discourage screwing around with <ctor>.prototype.constructor. That always felt like a major wart IMO. Making it ReadOnly/DontDelete would be icing on the cake. Not sure if we can squeeze it into max/min, but I like it. Classes are a leaky abstraction, as you said, but I think the major gain is making it easy to do things the right way, even if we don't really add much functionality.

The issue is "what is the right way?" I agree that classes shouldn't be too little veneer on oonstructor functions, though. Locking down the class constructor's .prototype is ok with me. It helps us move toward self-hosting the built-ins.

Does it? It's strictly more expressive to leave it unlocked; you can always use Object.defineProperty to lock it down after the fact. That should be enough to self-host the built-ins, unless I'm missing something.

What I don't like about forcing everyone to use .constructor is that maybe they don't want their class to expose the constructor. Sometimes you want to build an abstraction that maintains control over its instances. You might want to use the classy constructor pattern internally, but only expose abstract API's that internally construct instances, and then you don't want people mucking with the constructor. Were it up to me, I'd prefer a more mirror-based approach for exposing the constructor of instances, so the creator of the API can control whether they want to expose it. I recognize the C.prototype.constructor idiom already exists, but it's a weak idiom. I'm not crazy about the idea of strengthening a problematic but currently unreliable and rarely used idiom.

# Domenic Denicola (9 years ago)

On Mar 27, 2012, at 13:10, "David Herman" <dherman at mozilla.com> wrote:

I recognize the C.prototype.constructor idiom already exists, but it's a weak idiom. I'm not crazy about the idea of strengthening a problematic but currently unreliable and rarely used idiom.

Dave

Speaking as a dev, I would like this idiom to be stronger (i.e. be there by default in a classy world). One use case that immediately springs to mind is try { } catch (e) { switch(e.constructor) { } }, which we've used a couple times in the absence of Mozilla-style exception guards.

Your concern about possibly not wanting to expose the constructor makes a lot of sense, but perhaps we could leave .prototype.constructor configurable and let people in such a situation explicitly delete it.

# Herby Vojčík (9 years ago)

Domenic Denicola wrote:

On Mar 27, 2012, at 13:10, "David Herman"<dherman at mozilla.com> wrote:

I recognize the C.prototype.constructor idiom already exists, but it's a weak idiom. I'm not crazy about the idea of strengthening a problematic but currently unreliable and rarely used idiom.

Dave

Speaking as a dev, I would like this idiom to be stronger (i.e. be there by default in a classy world). One use case that immediately springs to mind is try { } catch (e) { switch(e.constructor) { } }, which we've used a couple times in the absence of Mozilla-style exception guards.

Your concern about possibly not wanting to expose the constructor makes a lot of sense, but perhaps we could leave .prototype.constructor configurable and let people in such a situation explicitly delete it.

+1 (I wonder why they did not proposed this, they used it for the methods as well)

# Russell Leggett (9 years ago)

On Tue, Mar 27, 2012 at 1:09 PM, David Herman <dherman at mozilla.com> wrote:

On Mar 26, 2012, at 10:32 PM, Brendan Eich wrote:

This is actually one of the reasons I still come down on constructor over new - I'd really like to discourage screwing around with <ctor>.prototype.constructor. That always felt like a major wart IMO. Making it ReadOnly/DontDelete would be icing on the cake. Not sure if we can squeeze it into max/min, but I like it. Classes are a leaky abstraction, as you said, but I think the major gain is making it easy to do things the right way, even if we don't really add much functionality.

The issue is "what is the right way?" I agree that classes shouldn't be too little veneer on oonstructor functions, though. Locking down the class constructor's .prototype is ok with me. It helps us move toward self-hosting the built-ins.

Does it? It's strictly more expressive to leave it unlocked; you can always use Object.defineProperty to lock it down after the fact. That should be enough to self-host the built-ins, unless I'm missing something.

What I don't like about forcing everyone to use .constructor is that maybe they don't want their class to expose the constructor. Sometimes you want to build an abstraction that maintains control over its instances. You might want to use the classy constructor pattern internally, but only expose abstract API's that internally construct instances, and then you don't want people mucking with the constructor. Were it up to me, I'd prefer a more mirror-based approach for exposing the constructor of instances, so the creator of the API can control whether they want to expose it. I recognize the C.prototype.constructor idiom already exists, but it's a weak idiom. I'm not crazy about the idea of strengthening a problematic but currently unreliable and rarely used idiom.

Removing/hiding it is just about the only reason I can think of touching it. Right now its a half baked promise of what the constructor is. I would like to have it either be correct or not there at all. I mean, yes, I suppose you could have some tricks with changing it to something else, but to me its mostly only useful at all if it can be relied on. This would be one step toward making it reliable. OTOH, I can see how it would be useful to remove it. Can we make it writable:false, configurable:true? Of course, that would only be a minor deterrent from changing it around, but maybe just enough?

# Brendan Eich (9 years ago)

Russell Leggett wrote:

Can we make it writable:false, configurable:true? Of course, that would only be a minor deterrent from changing it around, but maybe just enough?

This has zero integrity, so is IMHO worse than worthless -- it's misleading. Not that anyone will count on it, but stopping assignments while allowing reconfiguration or deletion and redefinition does not smell right.

To answer Dave's question, I meant that classes without any extra Object.defineProperty calls can be used to implement built-ins. Not a huge win but seemingly better than requiring Object.defineProperty for that case -- assuming class declarations create a constructor property on the class prototype.

If we want class C {} to create no C.prototype.constructor back-link to C, then we're not sugaring the prototypal pattern. That seems like too much innovation instead of desugaring, but you could model it via delete C.prototype.constructor after class C{} assuming constructor is configurable.

Of course one could argue based on desugaring to user-defined (not built-in) functions that C.prototype.constructor should be configurable and writable:

js> function f(){}

js> Object.getOwnPropertyDescriptor(f, 'prototype') ({configurable:false, enumerable:false, value:{}, writable:true}) js> Object.getOwnPropertyDescriptor(f.prototype, 'constructor') ({configurable:true, enumerable:false, value:function f() {}, writable:true})

# Russell Leggett (9 years ago)

On Tue, Mar 27, 2012 at 2:59 PM, Brendan Eich <brendan at mozilla.com> wrote:

Russell Leggett wrote:

Can we make it writable:false, configurable:true? Of course, that would only be a minor deterrent from changing it around, but maybe just enough?

This has zero integrity, so is IMHO worse than worthless -- it's misleading. Not that anyone will count on it, but stopping assignments while allowing reconfiguration or deletion and redefinition does not smell right.

I thought you'd probably say that, it was just a thought.

One thing we could do, is leave constructor writable/configurable, and then add a new private name property [ctor] that is always reliable. This would give people something to work with without really touching prototype.constructor. Of course, that doesn't help David's case. He really would need more of a mirror API to achieve that.

# Kevin Smith (9 years ago)

Getting back to the "new" vs. "constructor" issue:

The only real practical benefit to using "constructor" is that, if we use "new", it makes it a little bit harder to create a method named "new":

class C {
  constructor() {}
  new() {}
}

// vs

class C {
  new() {}
  "new"() {}
}

Otherwise (per Dave's suggestions) they are equivalent.

Since

  1. The whole point of classes is to provide syntactic sugar for what is already possible in the language, and

  2. There will be far more constructors (no matter how they are spelled) than "new" methods, and

  3. It would be rather bizarre to have a "new" method on a class instance anyway, and

  4. "constructor" is positively cringe-worthy : )

I conclude that on balance, we will get more benefit from using "new" than from using "constructor".

Of course, I would be happy to use either one (ever-so-slight cringing notwithstanding). ; )

# Allen Wirfs-Brock (9 years ago)

On Mar 27, 2012, at 11:59 AM, Brendan Eich wrote:

Russell Leggett wrote:

Can we make it writable:false, configurable:true? Of course, that would only be a minor deterrent from changing it around, but maybe just enough?

This has zero integrity, so is IMHO worse than worthless -- it's misleading. Not that anyone will count on it, but stopping assignments while allowing reconfiguration or deletion and redefinition does not smell right.

I think {writeable: false, configurable: true} is, in general, a great attribute combination. That's why I keep spec'ing things to use it. It isn't about integrity guarantees. It's about reinforcing the intended normal application level usage intent of a feature while preserving the ability perform reflective metaprogramming. It is about more clearly stratify the application and meta levels of ES. It's about = keeping straight which strata = belongs in.

JavaScript has always (I assume) allowed = to be used to create properties.

var obj = {}; obj.x = 42; obj.y = functionI) {return this.x*2};

The problem with such assignments is there is no clue whether x and y intended to used fixed data members of the object, expando data members, dynamic keys of a hash map data structure, or in the case of y is it a method or simply a data element with a first class function value. y even shows up in for-in data element enumerations (we infer from array behavior that for-in was intended to iterate over the data elements of an object and not the behavioral elements (eg methods).

But if I use concise method definitions in object literals or class declarations (or Object.defineMethod) I'm making a stronger statement about function valued properties. I'm explicitly saying that these properties are to be treated as behavioral elements not as data elements. Setting the attributes of concisely defined methods to {enumerable: false, writable:false, configurable: true} helps me guide the users of abstraction into usage patterns.

By setting writable: false we are saying, don't use the assignment operator to modify the value of this property. It isn't data. If you try to, the assignment will be ignored (or throw in strict mode). This applies whether you are directly accessing such a method on an object created by an object literal or operating upon an instance that is inheriting the property from a prototype. However, it does "freeze" the method. If you need to do meta programing involving such methods you still can. You just have to use Object.defineProperty/defineMethods. You have to act like you know what you are doing if you want to manipulate a property that has been explicitly declared as a method.

To answer Dave's question, I meant that classes without any extra Object.defineProperty calls can be used to implement built-ins. Not a huge win but seemingly better than requiring Object.defineProperty for that case -- assuming class declarations create a constructor property on the class prototype.

Chapter 15 built-ins have prototype.constructor that are writable and configurable. It is the prototype property that is non-writable and non-configurable for built-in constructors.

If we want class C {} to create no C.prototype.constructor back-link to C, then we're not sugaring the prototypal pattern. That seems like too much innovation instead of desugaring, but you could model it via delete C.prototype.constructor after class C{} assuming constructor is configurable.

In all the discussion about deleting (or not having) a "constructor" property on the prototype, I think people are over-looking (I haven't seen it mentioned) that if a subclass is going to do a super constructor call (whether the name "constructor" is syntactically explicit or implicit) then their better damn well be a property named "constructor" somewhere above it on the [[Prototype]] chain.

Of course one could argue based on desugaring to user-defined (not built-in) functions that C.prototype.constructor should be configurable and writable:

This is actually how the the maximally minimal class strawman currently defines it and for exactly the reason that it is what function() {} does. However, this thread has actually convinced me that it probably should be configurable and non-writable (and, of course, non-enumerable).

We shouldn't want to see this:

class C () { ... //methods } C.prototype.constructor = null;

if somebody has a good enough reason for this they should mean it enough to say

Object.defineProperty(C.prototype,"constructor",{value: null});