I noted some open issues on "Classes with Trait Composition"

# Brendan Eich (13 years ago)

strawman:classes_with_trait_composition#open_issues

This looks pretty good at a glance, but it's a lot, and it's new.

I have to say this reminds me of ES4 classes. That's neither bad nor good, but it's not just superficial, as far as I can tell (and I was reading specs then and now).

On the other hand, I'm in no rush to standardize something this complex and yet newly strawman-spec'ed and yet unimplemented. So we may as well take our time, learn from history, and go around the karmic wheel again for another few years...

I'm not against classes as a near-term objective, but in order to be near-term and not to unwind in committee, I believe they have to be dead simple and prototypal, with very few knobs, bells and whistles. Factoring out privacy and leaving constructor in charge of per-instance property setting, as it is in ES5, would IMHO help.

# Brendan Eich (13 years ago)

On May 15, 2011, at 10:01 PM, Brendan Eich wrote:

strawman:classes_with_trait_composition#open_issues

This looks pretty good at a glance, but it's a lot, and it's new.

Looking closer, I have to say something non-nit-picky that looks bad and smells like committee:

strawman:classes_with_trait_composition#inheritance

Two kinds of inheritance, depending on the dynamic type of the result of evaluating the //MemberExpression// on the right of ''extends''? That will be confusing.

Is the traits-composition way really needed in this proposal? If so, then please consider not abuse ''extends'' to mean ''compose'' depending on dynamic type of result of expression to its right.

# Dmitry A. Soshnikov (13 years ago)

On 16.05.2011 10:49, Brendan Eich wrote:

On May 15, 2011, at 10:01 PM, Brendan Eich wrote:

strawman:classes_with_trait_composition#open_issues

This looks pretty good at a glance, but it's a /lot/, and it's new.

Looking closer, I have to say something non-nit-picky that looks bad and smells like committee:

strawman:classes_with_trait_composition#inheritance

Two kinds of inheritance, depending on the dynamic type of the result of evaluating the //MemberExpression// on the right of ''extends''? That will be confusing.

Is the traits-composition way really needed in this proposal? If so, then please consider not abuse ''extends'' to mean ''compose'' depending on dynamic type of result of expression to its right.

Some simple examples of all use-cases would are needed I think.

Regarding new keyword for the constructor (aka initializer), after all, it als may be OK. E.g. Ruby uses new as exactly the method of a class -- Array.new, Object.new, etc. Though, constructor is also good yeah.

Regarding two inheritance types, I think better to make nevertheless one inheritance type -- linear (by prototype chain). And to make additionally small reusable code units -- mixins or traits -- no matter. Thus, of course if they will also be delegation-based and not just copy-own-properties, then we automatically get a sort of multiple inheritance.

So again, better to have something like this:

class Bar extends Foo { includes Comparable }

where Comparable is a trait/mixin. It becomes an ancestor of the instance (via some hidden-class), E.g.:

let bar = Bar.new(...args)

Then we have the following hierarchy:

bar -> HiddenClass -> Foo | | Comparable

I.e. beside the vertical ("tower") inheritance at each link of a chain we also have horizontal inheritance via mixins. Thus HiddenClass before going to its prototype chain, first try to find the property in the horizontal chain, i.e. in Comparable.

A simple object may also mix some functionality without using a class (if a user still needs pure prototype-based code reuse):

let bar = Object.mixing(Object.create(foo), Comparable);

Delegation-based mixins though can be implemented as a library using proxies (example: DmitrySoshnikov/es-laboratory/blob/master/examples/mixin.js, implementation: DmitrySoshnikov/es-laboratory/blob/master/src/mixin.js, notice I also used Object.new :)).

So we may actually understand what's going on by looking at the grammar, but nevertheless, if it's not hard, some simple examples would be nice.

Besides, I restore my previous syntactic proposal (Coffee/Ruby-like again) with a simple rule: (1) this evaluated in the class body is the class, (2) this evaluated in the instance method body is the instance. These two rules seems quite simple, not-overheaded, and the class definition looks like as an object initialiser. I used @ as a sugar, though it's not essential currently. Also, notice, I used initialize for new or constructor, since it seems more exactly describes the essence of the method: dmitrysoshnikov.com/scheme-on-coffee/class.html

Dmitry.

# Brendan Eich (13 years ago)

On May 16, 2011, at 4:54 AM, Dmitry A. Soshnikov wrote:

On 16.05.2011 10:49, Brendan Eich wrote:

On May 15, 2011, at 10:01 PM, Brendan Eich wrote:

strawman:classes_with_trait_composition#open_issues

This looks pretty good at a glance, but it's a lot, and it's new.

Looking closer, I have to say something non-nit-picky that looks bad and smells like committee:

strawman:classes_with_trait_composition#inheritance

Two kinds of inheritance, depending on the dynamic type of the result of evaluating the //MemberExpression// on the right of ''extends''? That will be confusing.

Is the traits-composition way really needed in this proposal? If so, then please consider not abuse ''extends'' to mean ''compose'' depending on dynamic type of result of expression to its right.

Some simple examples of all use-cases would are needed I think.

Regarding new keyword for the constructor (aka initializer), after all, it als may be OK. E.g. Ruby uses new as exactly the method of a class -- Array.new, Object.new, etc. Though, constructor is also good yeah.

My point is not to bikeshed, rather (a) to name existing prototype properties minimally, (b) to avoid preempting other names.

Good, bad, or in between, the prototypal pattern in JS for constructor C binds C.prototype.constructorr to C. It does not bind C.prototype.new.

Regarding two inheritance types, I think better to make nevertheless one inheritance type -- linear (by prototype chain). And to make additionally small reusable code units -- mixins or traits -- no matter. Thus, of course if they will also be delegation-based and not just copy-own-properties, then we automatically get a sort of multiple inheritance.

Self has multiple prototypes and you can use them for all kinds of inheritance patterns.

Parents are Shared Parts...

Organizing programs without classes

Delegation-based mixins though can be implemented as a library using proxies (example: DmitrySoshnikov/es-laboratory/blob/master/examples/mixin.js, implementation: DmitrySoshnikov/es-laboratory/blob/master/src/mixin.js, notice I also used Object.new :)).

Proxies are too costly, though. They always have a handler full of traps. The idea with classes is to capture prototypal inheritance as used today. The idea with traits is to make composition flexible, with fast failure on conflict and tools to rename around conflicts.

Putting classes and traitts together should be doable but it shouldn't use the same sytnax (extends) and it shouldn't require proxies.

# Dmitry A. Soshnikov (13 years ago)

On 16.05.2011 19:02, Brendan Eich wrote:

On May 16, 2011, at 4:54 AM, Dmitry A. Soshnikov wrote:

On 16.05.2011 10:49, Brendan Eich wrote:

On May 15, 2011, at 10:01 PM, Brendan Eich wrote:

strawman:classes_with_trait_composition#open_issues

This looks pretty good at a glance, but it's a /lot/, and it's new.

Looking closer, I have to say something non-nit-picky that looks bad and smells like committee:

strawman:classes_with_trait_composition#inheritance

Two kinds of inheritance, depending on the dynamic type of the result of evaluating the //MemberExpression// on the right of ''extends''? That will be confusing.

Is the traits-composition way really needed in this proposal? If so, then please consider not abuse ''extends'' to mean ''compose'' depending on dynamic type of result of expression to its right.

Some simple examples of all use-cases would are needed I think.

Regarding new keyword for the constructor (aka initializer), after all, it als may be OK. E.g. Ruby uses new as exactly the method of a class -- Array.new, Object.new, etc. Though, constructor is also good yeah.

My point is not to bikeshed, rather (a) to name existing prototype properties minimally, (b) to avoid preempting other names.

Good, bad, or in between, the prototypal pattern in JS for constructor C binds C.prototype.constructorr to C. It does not bind C.prototype.new.

Yeah, agreed.

Regarding two inheritance types, I think better to make nevertheless one inheritance type -- linear (by prototype chain). And to make additionally small reusable code units -- mixins or traits -- no matter. Thus, of course if they will also be delegation-based and not just copy-own-properties, then we automatically get a sort of multiple inheritance.

Self has multiple prototypes and you can use them for all kinds of inheritance patterns.

Parents are Shared Parts... citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.28.1447&rep=rep1&type=pdf

Organizing programs without classes citeseer.ist.psu.edu/viewdoc/download;jsessionid=77703261C2A470793139A1F79D8467AC?doi=10.1.1.127.5320&rep=rep1&type=pdf

Yes, I've heard about Self'ish multi-protos but don't know the lang, though have heard that JS has taken some things from it. Thanks for the paper.

Delegation-based mixins though can be implemented as a library using proxies (example: DmitrySoshnikov/es-laboratory/blob/master/examples/mixin.js, implementation: DmitrySoshnikov/es-laboratory/blob/master/src/mixin.js, notice I also used Object.new :)).

Proxies are too costly, though. They always have a handler full of traps.

Yep, it was just an example.

The idea with classes is to capture prototypal inheritance as used today.

Yeah, classes are just a sugar in this case, the same as in Coffee, the same as in Python.

The idea with traits is to make composition flexible, with fast failure on conflict and tools to rename around conflicts.

Yes, but IMO the most interesting for programmers the ability to mix reusable code. The conflict-resolver is derived. And if to implement this conflict-resolve traits will have to be just things for static augmentation of objects with own properties, I think I choose better just mixins.

Dmitry.

# Mark S. Miller (13 years ago)

On Sun, May 15, 2011 at 10:01 PM, Brendan Eich <brendan at mozilla.com> wrote:

strawman:classes_with_trait_composition#open_issues

That wiki page has no had extensive revisions in light of recent discussions with Brendan, Allen, Dave Herman, and Bob Nystrom. It derives from previous discussions with Allen, Bob, and Peter Hallam. I have tried to capture here as best as I could the consensus that has emerged from these discussions.

All this derives from earlier discussions that also included Waldemar, Alex Russell, Arv, and Tom Van Cutsem. And the experience of the Traceur project made a significant contribution. This has all had a long history so if I've left out some key contributors, please let me know, thanks.

This looks pretty good at a glance, but it's a lot, and it's new.

It's much less now! The main effect of all the recent feedback was to find opportunities to remove things. What remains is mostly just a way to express the familiar pattern by which JavaScript programmers manually express class-like semantics using prototypes. The result interoperates in both directions with such old code: a class can inherit from a traditional constructor function and vice versa.

I have to say this reminds me of ES4 classes. That's neither bad nor good, but it's not just superficial, as far as I can tell (and I was reading specs then and now).

It definitely had an influence. There were many things I liked about ES4 classes.

On the other hand, I'm in no rush to standardize something this complex and yet newly strawman-spec'ed and yet unimplemented. So we may as well take our time, learn from history, and go around the karmic wheel again for another few years...

I'm not against classes as a near-term objective, but in order to benear-term and not to unwind in committee, I believe they have to be dead simple and prototypal, with very few knobs, bells and whistles.

I am indeed proposing this as a near term objective. The usual caveats apply: we are asking the committee to approve the general shape presented by this strawman, with syntactic and semantic refinements expected to continue, for this as for all other proposals, after May.

Brendan, with all the simplifications since you posted this email, in your opinion, have we achieved the level of simplicity needed?

Factoring out privacy

Done.

and leaving constructor in charge of per-instance property setting, as it is in ES5,

Done.

would IMHO help.

Hope so ;).

I do understand that this page may be hard to appreciate without motivation and examples. I'm hoping these are coming soon.

# Mark S. Miller (13 years ago)

On Sun, May 15, 2011 at 11:49 PM, Brendan Eich <brendan at mozilla.com> wrote:

On May 15, 2011, at 10:01 PM, Brendan Eich wrote:

strawman:classes_with_trait_composition#open_issues

This looks pretty good at a glance, but it's a lot, and it's new.

Looking closer, I have to say something non-nit-picky that looks bad and smells like committee:

strawman:classes_with_trait_composition#inheritance

Two kinds of inheritance, depending on the dynamic type of the result of evaluating the //MemberExpression// on the right of ''extends''? That will be confusing.

This smell is actually just my fault; it did not derive from ideas arrived at in meetings. In any case, it is gone. "super(x, y);" is now always simply equivalent to "Superclass.call(this, x, y);", but as if using the original rather than the current binding of Function.prototype.call.

Is the traits-composition way really needed in this proposal? If so, then please consider not abuse ''extends'' to mean ''compose'' depending on dynamic type of result of expression to its right.

All dependencies on traits have been separated into a separate strawman, extending this one, but not to be proposed until after ES-next. The only inheritance in this one is traditional JS prototypal inheritance.

# Mark S. Miller (13 years ago)

On Mon, May 16, 2011 at 4:54 AM, Dmitry A. Soshnikov < dmitry.soshnikov at gmail.com> wrote: [...]

Some simple examples of all use-cases would are needed I think.

Absolutely agree. I hope they are coming soon. Watch this space ;).

Regarding new keyword for the constructor (aka initializer), after all, it als may be OK. E.g. Ruby uses new as exactly the method of a class -- Array.new, Object.new, etc. Though, constructor is also good yeah.

The history here is interesting. An earlier unreleased version of the Traceur compiler used "constructor". When we saw Allen's use of "new" in one of the object-literal-based class proposals, it seemed like a good idea so we switched to that. In light of Brendan's criticism, we realized we should return to "constructor" -- it's an elegant pun.

Regarding two inheritance types, I think better to make nevertheless one inheritance type -- linear (by prototype chain).

Done.

And to make additionally small reusable code units -- mixins or traits -- no matter. Thus, of course if they will also be delegation-based and not just copy-own-properties, then we automatically get a sort of multiple inheritance.

Gone. Or rather, postponed into a strawman that will not be proposed till after ES-next.

[...]

# Bob Nystrom (13 years ago)

On Mon, May 16, 2011 at 8:02 AM, Brendan Eich <brendan at mozilla.com> wrote:

On May 16, 2011, at 4:54 AM, Dmitry A. Soshnikov wrote:

Regarding new keyword for the constructor (aka initializer), after all, it als may be OK. E.g. Ruby uses new as exactly the method of a class -- Array.new, Object.new, etc. Though, constructor is also good yeah.

My point is not to bikeshed, rather (a) to name existing prototype properties minimally, (b) to avoid preempting other names.

Using "new" for the constructor is one of my favorite feature's of Allen's proposal. Things I like about it:

  1. It's terse. Since almost every class defines a ctor, this is helpful. "constructor" is a mouthful and repeating the full class name (like in Java, C++, etc.) is redundant.
  2. I think it's pretty clear to a user what's going on.
  3. It avoids stealing a valid identifier. You can't define a method named "new" but "constructor" is a valid property name.

Good, bad, or in between, the prototypal pattern in JS for constructor C binds C.prototype.constructorr to C. It does not bind C.prototype.new.

That's true, but I hope for classes is to hide just that machinery. There's no mention of "prototype" when you define a property on that object inside a class but that's what happens under the hood.

My hope is that users should almost never need to know or care that C.prototype.constructor is bound to C. Instead, what I'd like is for "class" to be a valid expression inside a class member that evaluates to C. Instead of:

class math.geometry.Circle { class var pi = 3; new(radius) { this.radius = radius; }

get circumference() { return this.radius * 2 * math.geometry.Circle.pi; } }

Be able to do:

class math.geometry.Circle { class var pi = 3; new(radius) { this.radius = radius; }

get circumference() { return this.radius * 2 * class.pi; // <--- } }

# David Herman (13 years ago)

Using "new" for the constructor is one of my favorite feature's of Allen's proposal. Things I like about it:

  1. It's terse. Since almost every class defines a ctor, this is helpful. "constructor" is a mouthful and repeating the full class name (like in Java, C++, etc.) is redundant.
  2. I think it's pretty clear to a user what's going on.
  3. It avoids stealing a valid identifier. You can't define a method named "new" but "constructor" is a valid property name.

Yes you can:

var obj = {
    new: function() { print("hey what's up") }
}

To my mind, this slightly weakens the case for |new| over |constructor|. You'd be special-casing the syntax not to create a property called "new" even though in object literals it does.

But I'm somewhat divided about this particular issue. You're right that the longer name hurts both for writing and reading. In both cases, the name is being special-cased (in either case, there's something special going on: the function defines the call and construct behavior of the class's constructor function, and both the instance and the prototype are given a property called |constructor|). But arguably there's something slightly less surprising about giving the special-case semantics to a reserved word, and of course only in the context of a class body. And yet, as I said above, there is something slightly weird about not creating a property called "new" when using the keyword |new|. (I think it would be a bad idea to have it create both a property called |constructor| and a property called |new|.)

# Brendan Eich (13 years ago)

On May 18, 2011, at 10:58 AM, Bob Nystrom wrote:

On Mon, May 16, 2011 at 8:02 AM, Brendan Eich <brendan at mozilla.com> wrote: On May 16, 2011, at 4:54 AM, Dmitry A. Soshnikov wrote:

Regarding new keyword for the constructor (aka initializer), after all, it als may be OK. E.g. Ruby uses new as exactly the method of a class -- Array.new, Object.new, etc. Though, constructor is also good yeah.

My point is not to bikeshed, rather (a) to name existing prototype properties minimally, (b) to avoid preempting other names.

Using "new" for the constructor is one of my favorite feature's of Allen's proposal. Things I like about it:

  1. It's terse. Since almost every class defines a ctor, this is helpful. "constructor" is a mouthful and repeating the full class name (like in Java, C++, etc.) is redundant.

The whole of class declaration is a mouthful. 'function' and 'prototype' are overlong too. We can try for conciseness in this one corner but it doesn't help in general. I think we need to weight the whole syntax and see where the verbosity and other pain points are in practice.

  1. I think it's pretty clear to a user what's going on.

Same with 'constructor', and that property must exist somehow. Why not be explicit and avoid having two names for one prototype property?

  1. It avoids stealing a valid identifier. You can't define a method named "new" but "constructor" is a valid property name.

3 is false in ES5. Reserved identifiers are valid property names. This is implemented in all the ES5-supporting browsers.

# Bob Nystrom (13 years ago)

On Wed, May 18, 2011 at 11:48 AM, Brendan Eich <brendan at mozilla.com> wrote:

The whole of class declaration is a mouthful. 'function' and 'prototype' are overlong too.

Agreed. One of the reasons I'm really excited about this proposal is that it addresses those:

// old and busted function SomeClass() {}; SomeClass.prototype.someMethod = function(arg) { ... }

// new hotness class SomeClass { someMethod(arg) { ... } }

We can try for conciseness in this one corner but it doesn't help in

general. I think we need to weight the whole syntax and see where the verbosity and other pain points are in practice.

Sure. I'm not stuck on "new", but in the chunks of sample code I've put together using "constructor" (which is what Traceur uses/did use for a good while) it did actually stick out as a syntactic wart, at least to me.

Same with 'constructor', and that property must exist somehow. Why not be

explicit and avoid having two names for one prototype property?

3 is false in ES5. Reserved identifiers are valid property names. This is implemented in all the ES5-supporting browsers.

Ah, my mistake. These are both fair points. It's too bad "constructor" is such a long word.

In the interests of considering all possibilities, one option would be to use no name at all for the constructor:

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

I find that simultaneously delightfully lightweight and terrifyingly obscure.

# Rick Waldron (13 years ago)

Just sort of curious... Given the following:

// old and busted function SomeClass() {}; SomeClass.prototype.someMethod = function(arg) { ... }

// new hotness class SomeClass { someMethod(arg) { ... } }

In the second form, how would I denote a method that is defined as an own property? Additionally, how would something like the following be handled?

function Ctor() { this.method = function() { return "own property"; }; return this; }

Ctor.prototype.method = function() { return "instance property"; };

var c = new Ctor();

console.log( c.method() // "own property" );

Thanks in advance :)

# Bob Nystrom (13 years ago)

I believe it would be something like:

class Ctor { public method() { return "own property"; }

method() { return "instance property"; } }

By default, members within a class body are declared on the prototype. Prefixing it with public places it on the new instance. Using public for this isn't carved in stone yet, but <some keyword> means "place the member

on the new instance" and that prefix can be applied to any kind of member: method, variable, constant, etc.

# Mark S. Miller (13 years ago)

On Wed, May 18, 2011 at 5:17 PM, Bob Nystrom <rnystrom at google.com> wrote:

I believe it would be something like:

class Ctor { public method() { return "own property"; }

method() { return "instance property"; } }

By default, members within a class body are declared on the prototype. Prefixing it with public places it on the new instance. Using public for this isn't carved in stone yet, but <some keyword> means "place the member on the new instance" and that prefix can be applied to any kind of member: method, variable, constant, etc.

Not quite. Per-instance member initialization only happens in the constructor, because the values of per-instance properties is generally data dependent on constructor parameters. The "public" (or whatever) and "private" declarations only prefix ForwardDeclarations, which are generally just comma separated lists of identifiers. The initialization in the constructor happens in the traditional ES5 imperative manner. Also, the public list is optional. It serves only a documentary purpose. So....

class Ctor { public method; // optional forward declaration of own property

method() { return "instance property"; }

constructor() { this.method = function() { return "own property"; }; } }

# Bob Nystrom (13 years ago)

On Wed, May 18, 2011 at 5:51 PM, Mark S. Miller <erights at google.com> wrote:

On Wed, May 18, 2011 at 5:17 PM, Bob Nystrom <rnystrom at google.com> wrote:

I believe it would be something like:

class Ctor { public method() { return "own property"; }

method() { return "instance property"; } }

By default, members within a class body are declared on the prototype. Prefixing it with public places it on the new instance. Using public for this isn't carved in stone yet, but <some keyword> means "place the member on the new instance" and that prefix can be applied to any kind of member: method, variable, constant, etc.

Not quite. Per-instance member initialization only happens in the constructor, because the values of per-instance properties is generally data dependent on constructor parameters. The "public" (or whatever) and "private" declarations only prefix ForwardDeclarations, which are generally just comma separated lists of identifiers. The initialization in the constructor happens in the traditional ES5 imperative manner. Also, the public list is optional. It serves only a documentary purpose. So....

class Ctor { public method; // optional forward declaration of own property

method() { return "instance property"; }

constructor() { this.method = function() { return "own property"; }; } }

Ah, OK. While it isn't necessary, it would be really convenient to be able to create per-instance properties by providing an initializer in the class body if those properties don't depend on "this" or ctor params:

class Point { public x = 0, y = 0; }

let p = new Point(); p.x; // 0

# Brendan Eich (13 years ago)

On May 18, 2011, at 5:57 PM, Bob Nystrom wrote:

class Point { public x = 0, y = 0; }

let p = new Point(); p.x; // 0

This is pretty rare, in my experience. A hard case? If the constructor does set x and y from parameters, then you have double-initialization. If some properties are non-writable, you can't do this. YAGNI?

# Mark S. Miller (13 years ago)

On Wed, May 18, 2011 at 6:29 PM, Brendan Eich <brendan at mozilla.com> wrote:

On May 18, 2011, at 5:57 PM, Bob Nystrom wrote:

class Point { public x = 0, y = 0; }

let p = new Point(); p.x; // 0

This is pretty rare, in my experience. A hard case? If the constructor does set x and y from parameters, then you have double-initialization. If some properties are non-writable, you can't do this. YAGNI?

+1. If you're gonna initialize them somewhere, why not always do so in the constructor and avoid special cases?

# Juan Ignacio Dopazo (13 years ago)

On Wed, May 18, 2011 at 4:10 PM, Bob Nystrom <rnystrom at google.com> wrote:

On Wed, May 18, 2011 at 11:48 AM, Brendan Eich <brendan at mozilla.com> wrote:

The whole of class declaration is a mouthful. 'function' and 'prototype' are overlong too.

Sure. I'm not stuck on "new", but in the chunks of sample code I've put together using "constructor" (which is what Traceur uses/did use for a good while) it did actually stick out as a syntactic wart, at least to me.

Same with 'constructor', and that property must exist somehow. Why not be

explicit and avoid having two names for one prototype property?

3 is false in ES5. Reserved identifiers are valid property names. This is implemented in all the ES5-supporting browsers.

Ah, my mistake. These are both fair points. It's too bad "constructor" is such a long word.

Nowadays most libraries that implement some sort of class wrapping use

lengthy names for the constructor and it's not that bad.

Mootools var Cat = new Class({ initialize: function(name){ this.name = name; } });

Prototype var Cat = Class.create({ initialize: function(name){ this.name = name; } });

Dojo var Cat = dojo.declare(null, { constructor: function(name){ this.name = name; } });

YUI var Cat = Y.Base.create('cat', null, [], { initializer: function(name){ this.name = name; } });

# Andreas Rossberg (13 years ago)

My apologies if this has been discussed to death before -- well, actually, I'd be surprised if it hasn't (pointers would be welcome).

I think it is worth noting that the baroque notation for defining constructors that we see in the C++ / Java / C# world primarily is an artefact of the desire to allow multiple constructors with overloading in those languages. We don't have that issue in JS, so I wonder why we cannot go for something more elegant? There is precedent in other OOPLs (off the top of my head, e.g. Scala and OCaml) for putting the constructor arguments on the class head directly, and executing the class body like a block when the constructor is invoked. AFAICS:

-- This approach is significantly slimmer (and, I'd argue, more readable) than the discussed alternatives, without needing any keywords:

class Point(x0, y0) { public x = x0 public y = y0 }

-- It naturally allows what Bob was suggesting:

class Point { // no argument list would be shorthand for (), just like when invoking new public x = 0 public y = 0 }

-- It avoids additional hoops with initializing const attributes:

class ImmutablePoint(x0, y0) { const x = x0 // just like elsewhere const y = y0 }

-- The constructor arguments naturally are in the scope of the entire object, so often you do not even need to introduce explicit (private) fields to store them:

class Point(x, y) { public function abs() { return Math.sqrt(xx, yy) } }

# Mark S. Miller (13 years ago)

yes we have a long history of consider this shape, in fact much longer than the current shape. The final state of proposals along these lines is < doku.php?id=strawman:classes_with_trait_composition&rev=1299750065

.

This approach actually lead to a much more elegant way of providing encapsulation: by the objects-as-closures pattern, where the methods are own methods of the instance lexically capturing the constructor's lexical context. However, we finally gave up on this for two closely related reasons:

  1. Starting from scratch, there's no problem engineering a VM to make objects-as-closures efficient, especially given the semi-static analysis that class proposal was designed to enable. However, JS VM implementors are not starting from scratch. Fitting such a new optimization into existing heavily optimized engines was thought to be a hard sell. Especially since all major VM implementors would need to agree.

  2. The conventional JS pattern is to place methods on the prototype, not the instance, and many felt that the main thing classes need to provide is a syntax to make this traditional semantics easier to express.

Another variation of your suggestion that Tom suggested is that you mix instance initialization and class/prototype initialization together in the class body. This obscures both time-of-execution and scope. Methods on the prototype do cannot have the constructor parameters in scope.

# David Herman (13 years ago)

Yes, we've talked about this. One of the issues I don't know how to resolve is if we want to allow the specification of class properties aka statics, then those need not to be in the scope of the constructor arguments, which ends up with very strange scoping behavior:

var x = "outer"
class C(x) {
    static foo = x // "outer" -- whoa!
}

I'm not 100% up on the current thinking of the group that's been working on classes, and whether they are including statics in the design, but I think they are.

# Brendan Eich (13 years ago)

On May 19, 2011, at 6:36 AM, Mark S. Miller wrote:

  1. Starting from scratch, there's no problem engineering a VM to make objects-as-closures efficient, especially given the semi-static analysis that class proposal was designed to enable. However, JS VM implementors are not starting from scratch. Fitting such a new optimization into existing heavily optimized engines was thought to be a hard sell. Especially since all major VM implementors would need to agree.

The hardship comes in part from mutability and fresh identity per constructor call of all the closure-methods. Specifying these as immutable and joined, optimizing their references to constructor parameters and per-instance variables to avoid capturing each constructor call's environment, would help.

  1. The conventional JS pattern is to place methods on the prototype, not the instance, and many felt that the main thing classes need to provide is a syntax to make this traditional semantics easier to express.

This is the big one.

# Bob Nystrom (13 years ago)

On Wed, May 18, 2011 at 6:29 PM, Brendan Eich <brendan at mozilla.com> wrote:

On May 18, 2011, at 5:57 PM, Bob Nystrom wrote:

class Point { public x = 0, y = 0; }

let p = new Point(); p.x; // 0

This is pretty rare, in my experience.

I just did some spelunking through some JS code. In about a dozen classes, I found 88 per-instance properties. Of them, 34 were initialized without reference to this or a ctor parameter, so about 39% of the properties could be expressed using the above syntax.

C# and Java both support this and I use it heavily.

A hard case? If the constructor does set x and y from parameters, then you

have double-initialization.

Well, if you are going to initialize it in the ctor, I wouldn't bother doing so outside of it too. I'm not proposing that an initializer is *required, *just that it's allowed. This does a few things for me:

  1. It lets me make it obvious which instance state is ctor-dependent and which isn't. This is fairly important to me because I find it makes it easier to understand a class's state.

  2. It's consistent with the rest of the member declarations which allow initializers:

class Foo { public onInstance = 0; onPrototype = 0; static onCtor = 0; }

Seems weird to me that the first line would be an error when the other two aren't.

  1. It's consistent with Java and C# which both allow this. Not that we need to mimic those languages, but it doesn't hurt to make the most of our users' expectations.

  2. It's terse:

class Stack { public items; constructor() { this.items = []; } ... }

class Stack { public items = []; ... }

# Brendan Eich (13 years ago)

On May 19, 2011, at 10:44 AM, Bob Nystrom wrote:

On Wed, May 18, 2011 at 6:29 PM, Brendan Eich <brendan at mozilla.com> wrote: On May 18, 2011, at 5:57 PM, Bob Nystrom wrote:

class Point { public x = 0, y = 0; }

let p = new Point(); p.x; // 0

This is pretty rare, in my experience.

I just did some spelunking through some JS code. In about a dozen classes, I found 88 per-instance properties. Of them, 34 were initialized without reference to this or a ctor parameter, so about 39% of the properties could be expressed using the above syntax.

Don't take this the wrong way ;-), but how many of those initializations were immediately overwritten by the constructor, unconditionally or even conditionally?

C# and Java both support this and I use it heavily.

A hard case? If the constructor does set x and y from parameters, then you have double-initialization.

Well, if you are going to initialize it in the ctor, I wouldn't bother doing so outside of it too. I'm not proposing that an initializer is required, just that it's allowed. This does a few things for me:

  1. It lets me make it obvious which instance state is ctor-dependent and which isn't. This is fairly important to me because I find it makes it easier to understand a class's state.

Good point.

  1. It's consistent with the rest of the member declarations which allow initializers:

class Foo { public onInstance = 0; onPrototype = 0; static onCtor = 0; }

Seems weird to me that the first line would be an error when the other two aren't.

Ditto.

  1. It's consistent with Java and C# which both allow this. Not that we need to mimic those languages, but it doesn't hurt to make the most of our users' expectations.

Meh :-P.

  1. It's terse:

class Stack { public items; constructor() { this.items = []; } ... }

class Stack { public items = []; ... }

You'd need a fresh [] evaluation per construction. Mark pointed out how this seems to change the evaluation rules for the immediate elements of the class body. Not fatal but another kind of inconsistency, along a different dimension.

# Bob Nystrom (13 years ago)

On Thu, May 19, 2011 at 10:50 AM, Brendan Eich <brendan at mozilla.com> wrote:

Don't take this the wrong way ;-), but how many of those initializations were immediately overwritten by the constructor, unconditionally or even conditionally?

Heh, zero, actually. We're pretty scrupulous here. The 34 examples fell roughly into these buckets:

  1. State that just always started a certain way: this.checked = false;
  2. Collections that are mutated, but never assigned: this.items = [];
  3. Objects used to compose a larger one: this.myButton = new Button("label");

If it was ever assigned to in the ctor using state passed in, or in a control flow path that was dependent on ctor arguments (or this), I didn't count it in the 34.

  1. It's terse:

class Stack { public items; constructor() { this.items = []; } ... }

class Stack { public items = []; ... }

You'd need a fresh [] evaluation per construction. Mark pointed out how this seems to change the evaluation rules for the immediate elements of the class body. Not fatal but another kind of inconsistency, along a different dimension.

I agree, this part feels a little weird. The fact that we're doing *anything

  • per-instance in the class body is probably the strangest part of this proposal since the other two objects touched by the class body (the ctor object and the prototype) are both singletons. Even declaring per-instance properties there feels a little strange, but I think it's generally worth that inelegance. <shrug>
# Brendan Eich (13 years ago)

On May 19, 2011, at 11:02 AM, Bob Nystrom wrote:

On Thu, May 19, 2011 at 10:50 AM, Brendan Eich <brendan at mozilla.com> wrote: Don't take this the wrong way ;-), but how many of those initializations were immediately overwritten by the constructor, unconditionally or even conditionally?

Heh, zero, actually. We're pretty scrupulous here. The 34 examples fell roughly into these buckets:

  1. State that just always started a certain way: this.checked = false;
  2. Collections that are mutated, but never assigned: this.items = [];
  3. Objects used to compose a larger one: this.myButton = new Button("label");

If it was ever assigned to in the ctor using state passed in, or in a control flow path that was dependent on ctor arguments (or this), I didn't count it in the 34.

Thanks for measuring, helpful beyond our qualitative arguments.

Here's another question: how many property initializations could be done straight from the parameter of the same name?

And here's the punchline: we are forgetting about parameter default values:

harmony:parameter_default_values

Apart from the desire to declare per-instance property names, do we really need default values if the constructor author could write default parameter values?

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

Now, Scala and CoffeeScript fans will object to repeating x and y three times. IIRC an earlier proposal even allowed for the concise syntax

class Point { constructor(this.x = 0, this.y = 0) { } ... }

The CoffeeScript syntax is even sweeter:

class Point { constructor(@x = 0, @y = 0); ... }

The objection here is that @ is coveted, for private names or decorators.

Perhaps we could use leading dot in formal parameter names to mean this.:

class Point { constructor(.x = 0, .y = 0); ... }

Comments welcome!

  1. It's terse:

class Stack { public items; constructor() { this.items = []; } ... }

class Stack { public items = []; ... }

You'd need a fresh [] evaluation per construction. Mark pointed out how this seems to change the evaluation rules for the immediate elements of the class body. Not fatal but another kind of inconsistency, along a different dimension.

I agree, this part feels a little weird. The fact that we're doing anything per-instance in the class body is probably the strangest part of this proposal since the other two objects touched by the class body (the ctor object and the prototype) are both singletons. Even declaring per-instance properties there feels a little strange, but I think it's generally worth that inelegance. <shrug>

See if my parameter default value pitch does not provide a better alternative.

# Mark S. Miller (13 years ago)

On Thu, May 19, 2011 at 11:47 AM, Brendan Eich <brendan at mozilla.com> wrote:

On May 19, 2011, at 11:02 AM, Bob Nystrom wrote:

On Thu, May 19, 2011 at 10:50 AM, Brendan Eich <brendan at mozilla.com>wrote:

Don't take this the wrong way ;-), but how many of those initializations were immediately overwritten by the constructor, unconditionally or even conditionally?

Heh, zero, actually. We're pretty scrupulous here. The 34 examples fell roughly into these buckets:

  1. State that just always started a certain way: this.checked = false;
  2. Collections that are mutated, but never assigned: this.items = [];
  3. Objects used to compose a larger one: this.myButton = new Button("label");

If it was ever assigned to in the ctor using state passed in, or in a control flow path that was dependent on ctor arguments (or this), I didn't count it in the 34.

Thanks for measuring, helpful beyond our qualitative arguments.

Here's another question: how many property initializations could be done straight from the parameter of the same name?

And here's the punchline: we are forgetting about parameter default values:

harmony:parameter_default_values

Apart from the desire to declare per-instance property names, do we really need default values if the constructor author could write default parameter values?

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

Now, Scala and CoffeeScript fans will object to repeating x and y three times. IIRC an earlier proposal even allowed for the concise syntax

class Point { constructor(this.x = 0, this.y = 0) { } ... }

I like Bob's "this.x" form best, especially when combined with your observation about default values.

The reason I prefer "this.x" rather than the terser ".x" or "@x" (even assuming away conflicts with other uses if "@"), is that I can imagine many programmers with ES < 6 experience, on seeing a "this.x" parameter for the first time, to be able to guess what it means.

# Brendan Eich (13 years ago)

On May 19, 2011, at 11:55 AM, Mark S. Miller wrote:

I like Bob's "this.x" form best, especially when combined with your observation about default values.

That's great -- if we want to shorthand harder, we can consider .x for this.x down the road.

The reason I prefer "this.x" rather than the terser ".x" or "@x" (even assuming away conflicts with other uses if "@"), is that I can imagine many programmers with ES < 6 experience, on seeing a "this.x" parameter for the first time, to be able to guess what it means.

Sure.

So I'm not sure you bought my argument that this means we don't need intiialisers for the public and private name declarations that are outside of the constructor head and body. And I don't want to twist Bob's arms. Let me know if this does relieve us from having to support initialisers evaluated per-construction yet declared outside the constructor.

# Mark S. Miller (13 years ago)

On Thu, May 19, 2011 at 12:02 PM, Brendan Eich <brendan at mozilla.com> wrote:

On May 19, 2011, at 11:55 AM, Mark S. Miller wrote:

I like Bob's "this.x" form best, especially when combined with your observation about default values.

That's great -- if we want to shorthand harder, we can consider .x for this.x down the road.

The reason I prefer "this.x" rather than the terser ".x" or "@x" (even assuming away conflicts with other uses if "@"), is that I can imagine many programmers with ES < 6 experience, on seeing a "this.x" parameter for the first time, to be able to guess what it means.

Sure.

So I'm not sure you bought my argument that this means we don't need intiialisers for the public and private name declarations that are outside of the constructor head and body. And I don't want to twist Bob's arms. Let me know if this does relieve us from having to support initialisers evaluated per-construction yet declared outside the constructor.

Even without this, I am opposed to initializers outside the constructor for the reason you already mentioned: confusion of execution time issues. If we allowed

class Stack {
  public items = [];
  //...
}

some experienced JS programmers without Java experience would look at this and sensibly expect the [] to be evaluated once per evaluation of the class definition, leading all stacks to initialize their own "items" variable to a shared initially empty list. For some purposes, this might even be useful. Others would sensibly expect it to be evaluated once per constructor call. Any resolution will give some sensible programmers rude surprises.

With the ability to say "this.x = 0" in the constructor parameters, this choice becomes painless. But IMO the choice is necessary regardless.

# Axel Rauschmayer (13 years ago)

Yes, we've talked about this. One of the issues I don't know how to resolve is if we want to allow the specification of class properties aka statics

This is a somewhat orthogonal issue:

Currently the implementation of a type is a constructor function. This always felt slightly odd to me, because the prototype seems the more obvious choice. Then instanceof would be simpler. Furthermore, type properties (class properties) would make more sense and could additionally be inherited from supertype to subtype (use case: a root type implements a method for subtyping that can be applied to itself to create the first subtype).

The changes would be as follows:

(1) Allow |new| not just for functions, but also for "type objects". The type object itself would be the prototype of each newly created instance, instance data would be added in a special method (e.g. "constructor" or "new"). (2) Allow "type objects" to be the rhs for instanceof (in addition to function). That is, if the rhs of instanceof is an object, look for it in the prototype chain.

Has this been considered?

Then (the similar) "classes" (as syntactic sugar for constructor functions etc.) would maybe not be needed. Still missing is convenient subtyping

The remaining missing functionality is subtyping, but that could be added as an API (let me know if I should give an example, it would look a lot like YUI, but with less impedance mismatch).

# Bob Nystrom (13 years ago)

On Thu, May 19, 2011 at 11:47 AM, Brendan Eich <brendan at mozilla.com> wrote:

Here's another question: how many property initializations could be done straight from the parameter of the same name?

Of the 88 fields I looked at, 10 were straight copies of a constructor argument. Another 4 are copies with an "if undefined then use this default instead" check. That may be biased a bit low since most of the code I looked at is UI widget-like stuff. Code that's more data-model oriented would probably have more cases where a class is just wrapping a bunch of arguments.

Apart from the desire to declare per-instance property names, do we really

need default values if the constructor author could write default parameter values?

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

That covers a different (though useful!) case. This would be helpful for the 4 cases above where a field is initialized to a constructor argument if given, or to a default value if not. For the 34 other cases, those fields don't come from the constructor arguments at all.

class Point { constructor(this.x = 0, this.y = 0) { } ... }

Yeah, that's in the document the proposal is linking to now ( docs.google.com/document/d/1gOFRSBOKtB8VjXC5LRhhNiGi1n1QQ6z1O6LyxjrOB3g/edit?hl=en_US). I'd like to roll that into the proposal as an optional refinement. I really really like this.

The objection here is that @ is coveted, for private names or decorators.

FWIW, my vote would be to keep that for decorators/annotations.

Perhaps we could use leading dot in formal parameter names to mean this.:

class Point { constructor(.x = 0, .y = 0); ... }

Comments welcome!

That's delightful!

# Mark S. Miller (13 years ago)

sent from android On May 19, 2011 3:09 PM, "Bob Nystrom" <rnystrom at google.com> wrote:

On Thu, May 19, 2011 at 11:47 AM, Brendan Eich <brendan at mozilla.com>

wrote:

Here's another question: how many property initializations could be done

straight from the parameter of the same name?

Of the 88 fields I looked at, 10 were straight copies of a constructor

argument. Another 4 are copies with an "if undefined then use this default instead" check. That may be biased a bit low since most of the code I looked at is UI widget-like stuff. Code that's more data-model oriented would probably have more cases where a class is just wrapping a bunch of arguments.

Apart from the desire to declare per-instance property names, do we

really need default values if the constructor author could write default parameter values?

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

That covers a different (though useful!) case. This would be helpful for

the 4 cases above where a field is initialized to a constructor argument if given, or to a default value if not. For the 34 other cases, those fields don't come from the constructor arguments at all.

class Point { constructor(this.x = 0, this.y = 0) { } ... }

Yeah, that's in the document the proposal is linking to now (

docs.google.com/document/d/1gOFRSBOKtB8VjXC5LRhhNiGi1n1QQ6z1O6LyxjrOB3g/edit?hl=en_US). I'd like to roll that into the proposal as an optional refinement. I really really like this.

Please do. I like it too. But yes, as an optional refinement.

# Luke Hoban (13 years ago)
  1. The conventional JS pattern is to place methods on the prototype, not the instance, and many felt that the main thing classes need to provide is a syntax to make this traditional semantics easier to express.

This is the big one.

Class syntax with class parameters instead of explicit constructors can still be defined to produce methods on the prototype, right?

Andreas' example:

class Point(x, y) { public function abs() { return Math.sqrt(xx, yy) } }

Can effectively desguar to:

function Point(x,y) { this.x = x; this.y = y; } Point.prototype.abs = function() { return Math.sqrt(this.xthis.x, this.ythis.y); }

Or, with private names, to a variant with the captured x and y stored as private fields on the instance. It's really just a syntax choice between explicit constructor functions and parameterized class definitions. There are many benefits to the latter, and many modern OO languages seem to be using this style effectively.

Luke

# Brendan Eich (13 years ago)

On May 19, 2011, at 2:10 PM, Mark S. Miller wrote:

On May 19, 2011 3:09 PM, "Bob Nystrom" <rnystrom at google.com> wrote:

class Point { constructor(this.x = 0, this.y = 0) { } ... }

Yeah, that's in the document the proposal is linking to now (docs.google.com/document/d/1gOFRSBOKtB8VjXC5LRhhNiGi1n1QQ6z1O6LyxjrOB3g/edit?hl=en_US). I'd like to roll that into the proposal as an optional refinement. I really really like this.

Please do. I like it too. But yes, as an optional refinement.

+1

And I'm +1 on including it normatively, FWIW.

You guys have been good sports, here is one last minor comment: The ExportableDefinition nonterminal produces this right-hand side: Identifier = Expression ; // provided data which works perfectly after class (now static, hrm, two minor comments then ;-). But without a keyword in front,

class Point { x = 0; ... }

is a bit of a mystery.

Is it an assignment expression-statement? No, no statements as ClassElements.

Could it be creating a prototype property, as other unprefixed property initialisers such as get and set accessor declarations, and property initialiser extensions declaring prototype methods, do? Yes, that's the intent.

But, I object (mildly), it does not look like a property initialiser. It looks like an assignment expression-statement.

I see the dilemma: if you use colon, you walk into class body as object initialiser body, with comma separation, etc. If you use equal sign, you avoid that.

But this is only for defining data properties on the class prototype. Is this case common? I can't think of a built-in class that has such a data property. It's very rare.

So how about YAGNI -- leave this out, it won't be needed enough and one can always write C.prototype.x = 0 or Object.defineProperty(C.prototype, 'x', ...) after the class declaration has been evaluated.

Or if you have a measured need based on some existing code, which I'd love to hear about: consider a prefixing keyword to make this look like an alternative to static x = 0.

Ok, second minor point. At least dherman and I really dug the use of class where static is now specified. We avoid unwanted baggage from other (and static as in typing) languages. We point directly to the value of the binding created by the class declaration or evaluated from an anonymous class declaration: the constructor.

Any strong reason why this change was made?

Thanks for considering,

# Mark S. Miller (13 years ago)

On Thu, May 19, 2011 at 3:08 PM, Brendan Eich <brendan at mozilla.com> wrote: [...]

You guys have been good sports, here is one last minor comment: The * ExportableDefinition* nonterminal produces this right-hand side:

  Identifier = Expression ;                            // provided data

which works perfectly after class (now static, hrm, two minor comments then ;-). But without a keyword in front,

class Point { x = 0; ... }

is a bit of a mystery.

Is it an assignment expression-statement? No, no statements as * ClassElements*.

Could it be creating a prototype property, as other unprefixed property initialisers such as get and set accessor declarations, and property initialiser extensions declaring prototype methods, do? Yes, that's the intent.

But, I object (mildly), it does not look like a property initialiser. It looks like an assignment expression-statement.

I see the dilemma: if you use colon, you walk into class body as object initialiser body, with comma separation, etc. If you use equal sign, you avoid that.

But this is only for defining data properties on the class prototype. Is this case common? I can't think of a built-in class that has such a data property. It's very rare.

So how about YAGNI -- leave this out, it won't be needed enough and one can always write C.prototype.x = 0 or Object.defineProperty(C.prototype, 'x', ...) after the class declaration has been evaluated.

Or if you have a measured need based on some existing code, which I'd love to hear about: consider a prefixing keyword to make this look like an alternative to static x = 0.

Bob initially used "var" for that purpose. I do not object to having such a short prefixing keyword. But ever since the "let is the new var" revolution, whenever my eye notices a "var", my immediate reaction is "Danger danger warning warning. Once we can, figure out what this code is doing and rewrite as using 'let' or 'const'." Once ES-next is real and people really can get rid of "var"s, I hope this reaction will become more common. Looking for bad old code to fix? Grep for "var". Anything that habituates us to "var", reducing this alarm reaction, seems bad.

That said, if we decide we really want a keyword here and can't come up with a better alternative than "var", I probably could be talked into it. And I'm happy to see this added as an option to the strawman.

Ok, second minor point. At least dherman and I really dug the use of classwhere static is now specified. We avoid unwanted baggage from other (and static as in typing) languages. We point directly to the value of the binding created by the class declaration or evaluated from an anonymous class declaration: the constructor.

Any strong reason why this change was made?

When I first saw this use of "class" I loved it. I never liked "static" and I disliked it even more after that. But the following syntactic case convinced me otherwise:

class A {
  class B {...}        // defines A.prototype.B as a class
  class class C {...}  // defines A.C as a class
  class D() {...}      // defines A.D as a function.
}

by contrast, I don't think the following creates any similar confusion

class A {
  class B {...}        // defines A.prototype.B as a class
  static class C {...} // defines A.C as a class
  static D() {...}     // defines A.D as a function.
}
# Brendan Eich (13 years ago)

On May 19, 2011, at 3:43 PM, Mark S. Miller wrote:

Or if you have a measured need based on some existing code, which I'd love to hear about: consider a prefixing keyword to make this look like an alternative to static x = 0.

Bob initially used "var" for that purpose. I do not object to having such a short prefixing keyword. But ever since the "let is the new var" revolution, whenever my eye notices a "var", my immediate reaction is "Danger danger warning warning. Once we can, figure out what this code is doing and rewrite as using 'let' or 'const'." Once ES-next is real and people really can get rid of "var"s, I hope this reaction will become more common. Looking for bad old code to fix? Grep for "var". Anything that habituates us to "var", reducing this alarm reaction, seems bad.

I note that Declaration is produced by ExportableDefinition produced by PublicPrototypePropertyDefinition. That means let, const, function, and nested class are all possible in a class body.

strawman:classes_with_trait_composition#member_declarations_and_definitions is light on details, but I took these to define local bindings usable in other ClassElements -- not to create prototype properties (or any kind of property on an object).

Did I miss something?

That said, if we decide we really want a keyword here and can't come up with a better alternative than "var", I probably could be talked into it. And I'm happy to see this added as an option to the strawman.

I'm still not sure we need to bind (enumerable by default, I just noticed -- a for-in hazard) data properties on the class prototype. If we do, I agree that var is not a good keyword to retask. We could make new keywords in context in the class body, I think. Dave mooted proto when we were talking about this. Just an idea.

When I first saw this use of "class" I loved it. I never liked "static" and I disliked it even more after that. But the following syntactic case convinced me otherwise:

class A {
  class B {...}        // defines A.prototype.B as a class
  class class C {...}  // defines A.C as a class
  class D() {...}      // defines A.D as a function.
}

Oh, so nested class declaration binds a prototype property? That is confusing. What do let, const, and function do? The other ClassElement production right-hand sides produce accessor and the new method property initialiser forms, not Declarations.

by contrast, I don't think the following creates any similar confusion

class A {
  class B {...}        // defines A.prototype.B as a class
  static class C {...} // defines A.C as a class
  static D() {...}     // defines A.D as a function.
}

Given a nested class declaration binding a prototype property, I see your point. But is that nested class semantic really the right one, and how does it related to the other Declaration variants?

# Mark S. Miller (13 years ago)

On Thu, May 19, 2011 at 3:00 PM, Luke Hoban <lukeh at microsoft.com> wrote:

  1. The conventional JS pattern is to place methods on the prototype, not the instance, and many felt that the main thing classes need to provide is a syntax to make this traditional semantics easier to express.

This is the big one.

Class syntax with class parameters instead of explicit constructors can still be defined to produce methods on the prototype, right?

Andreas' example:

class Point(x, y) { public function abs() { return Math.sqrt(xx, yy) } }

Hi Luke, given the above code, the semantics I should expect is that abs lexically captures the x and y in scope. Notice that abs nowhere makes any mention of "this", so I wouldn't expect it to be "this" sensitive. "this" sensitivity is one of the trickiest hazards of JavaScript, but so far it is only raised by functions that explicitly mention this.

Say have a point pt and wish to store its abs method in some data structure of ours, for purpose of calling it later:

var absFuncs = [];

function storeAbsFrom(pt) {
  absFuncs.push(pt.abs);
}

function getTotalAbs1() {
  let result = 0;
  for (let i = 0, len = absFuncs.length; i < len; i++) {
    result += absFuncs[i]();
  }
  return result;
}

function getTotalAbs2() {
  return absFuncs.reduce(0, function(sum, abs) {
    return sum + abs();
  });
}

If abs lexically captured x and y, both of the above getTotalAbs functions would work. Instead, with the suggested semantics, getTotalAbs1() returns NaN and getTotalAbs2() throws a TypeError.

Can effectively desguar to:

function Point(x,y) { this.x = x; this.y = y; } Point.prototype.abs = function() { return Math.sqrt(this.xthis.x, this.ythis.y); }

Or, with private names, to a variant with the captured x and y stored as private fields on the instance. It's really just a syntax choice between explicit constructor functions and parameterized class definitions. There are many benefits to the latter, and many modern OO languages seem to be using this style effectively.

Most other OO languages don't have the this-confusion hazards that JavaScript suffers from.

# Mark S. Miller (13 years ago)

On Thu, May 19, 2011 at 3:56 PM, Brendan Eich <brendan at mozilla.com> wrote:

On May 19, 2011, at 3:43 PM, Mark S. Miller wrote:

Or if you have a measured need based on some existing code, which I'd love

to hear about: consider a prefixing keyword to make this look like an alternative to static x = 0.

Bob initially used "var" for that purpose. I do not object to having such a short prefixing keyword. But ever since the "let is the new var" revolution, whenever my eye notices a "var", my immediate reaction is "Danger danger warning warning. Once we can, figure out what this code is doing and rewrite as using 'let' or 'const'." Once ES-next is real and people really can get rid of "var"s, I hope this reaction will become more common. Looking for bad old code to fix? Grep for "var". Anything that habituates us to "var", reducing this alarm reaction, seems bad.

I note that Declaration is produced by *ExportableDefinition *produced by PublicPrototypePropertyDefinition. That means let, const, function, and nested class are all possible in a class body.

Yes.

strawman:classes_with_trait_composition#member_declarations_and_definitionsis light on details, but I took these to define local bindings usable in other ClassElements -- not to create prototype properties (or any kind of property on an object).

Did I miss something?

Yes. An unannotated Declaration defines a prototype property of that name.

class Point {
  const x = 0;
  //...
}

defines a non-writable, enumerable, non-configurable "x" property on Point.prototype, as if by

Object.defineProperty(Point.prototype, 'x', {
  value: 0, writable: false, enumerable: true, configurable: false
});

That said, if we decide we really want a keyword here and can't come up with a better alternative than "var", I probably could be talked into it. And I'm happy to see this added as an option to the strawman.

I'm still not sure we need to bind (enumerable by default, I just noticed -- a for-in hazard) data properties on the class prototype.

I agree that data on the prototype is comparatively rare. But symmetry and consistency sometimes lead us to supporting rare cases, merely to avoid introducing special cases that prohibit them.

If we do, I agree that var is not a good keyword to retask. We could make new keywords in context in the class body, I think. Dave mooted proto when we were talking about this. Just an idea.

I prefer "proto" to "var", so I could probably be talked into that as well.

When I first saw this use of "class" I loved it. I never liked "static" and I disliked it even more after that. But the following syntactic case convinced me otherwise:

class A {
  class B {...}        // defines A.prototype.B as a class
  class class C {...}  // defines A.C as a class
  class D() {...}      // defines A.D as a function.
}

Oh, so nested class declaration binds a prototype property? That is confusing. What do let, const, and function do?

The same thing. An unprefixed let defines a writable enumerable configurable property on the prototype. An unprefixed const as above defines a non-writable, enumerable, non-configurable property on the prototype. An unprefixed class or function defines a non-enumerable ...

All of these, as well as the other ExportableDefinitions, define corresponding props on the constructor when prefixed by something -- currently "static".

The other ClassElement production right-hand sides produce accessor and the new method property initialiser forms, not Declarations.

As a ClassElement, "x = y;" is just shorthand for "let x = y;". Likewise "static x = y;" is just shorthand for "static let x = y;".

by contrast, I don't think the following creates any similar confusion

class A {
  class B {...}        // defines A.prototype.B as a class
  static class C {...} // defines A.C as a class
  static D() {...}     // defines A.D as a function.
}

Given a nested class declaration binding a prototype property, I see your point. But is that nested class semantic really the right one, and how does it related to the other Declaration variants?

Symmetric and consistent without special cases.

Also, Gilad Bracha has shown many cool patterns in Newspeak leveraging the ability to inherit a default nested class but being able to override it by a like-named nested class in a subclass. Once you get used to it, it's actually much more natural and expressive than static nested classes.

# Bob Nystrom (13 years ago)

On Thu, May 19, 2011 at 3:08 PM, Brendan Eich <brendan at mozilla.com> wrote:

class Point { x = 0; ... }

is a bit of a mystery.

Is it an assignment expression-statement? No, no statements as * ClassElements*.

Could it be creating a prototype property, as other unprefixed property initialisers such as get and set accessor declarations, and property initialiser extensions declaring prototype methods, do? Yes, that's the intent.

But, I object (mildly), it does not look like a property initialiser. It looks like an assignment expression-statement.

Agreed, completely. My first stab at this used var for declaring fields:

class Point { var x = 0; // x field on prototype }

I know Mark wishes var could be banished from the language completely. My goal would be to have let become the thing you use for variables and varthen becomes a "free" keyword we can then repurpose for something like this. I don't know if that would just sow confusion, but var seems like too nice of a keyword to go to waste, and it would be unambiguous inside a class since a class body doesn't allow arbitrary statements.

But this is only for defining data properties on the class prototype. Is this case common?

Closure code uses data-on-the-prototype pervasively for default values, like:

class Thermostat { var temp = 70; // on proto setTemp(temp) { this.temp = temp; // once you touch it, it becomes specific to the instance } }

I count nine examples of this just in Control.jsclosure-library.googlecode.com/svn/docs/closure_goog_ui_control.js.source.html (search for "prototype.content_"). It has the advantage of not wasting per-instance memory for values that are still the default, which is a neat trick that class-based languages can't do.

Or if you have a measured need based on some existing code, which I'd love

to hear about: consider a prefixing keyword to make this look like an alternative to static x = 0.

That was part of my motivation for using var. It seemed weird to have an adjective like static without a noun that it modifies. (I was also using function for methods for the same reason, though we've moved away from that.)

# Brendan Eich (13 years ago)

On May 19, 2011, at 4:24 PM, Mark S. Miller wrote:

strawman:classes_with_trait_composition#member_declarations_and_definitions is light on details, but I took these to define local bindings usable in other ClassElements -- not to create prototype properties (or any kind of property on an object).

Did I miss something?

Yes. An unannotated Declaration defines a prototype property of that name.

class Point {
  const x = 0;
  //...
}

defines a non-writable, enumerable, non-configurable "x" property on Point.prototype, as if by

Object.defineProperty(Point.prototype, 'x', {
  value: 0, writable: false, enumerable: true, configurable: false
});

I see. Then a couple of thoughts:

  1. The Declaration production being used here, when used in Statement contexts normally binds names lexically. Here it binds prototype properties. This is not consistent in a sense we might want to make consistent. Allen's strawman:concise_object_literal_extensions strawman provides initialiser extensions for controlling writable, configurable and enumerable.

  2. In this light, I do see the value of var instead of const: prior to Harmony, var at top level binds a writable, enumerable, non-configurable global property. True, Harmony gets the global object off the scope chain, but the precedent is there.

Taken together, I wonder whether we aren't abusing Declaration to bind properties.

That said, if we decide we really want a keyword here and can't come up with a better alternative than "var", I probably could be talked into it. And I'm happy to see this added as an option to the strawman.

I'm still not sure we need to bind (enumerable by default, I just noticed -- a for-in hazard) data properties on the class prototype.

I agree that data on the prototype is comparatively rare. But symmetry and consistency sometimes lead us to supporting rare cases, merely to avoid introducing special cases that prohibit them.

Sure, although mixing declaration syntax with property initialiser syntax in class body context invites two conflicting dimensions of consistency. That again makes me wonder whether class body should not consist only of property initialiser variants including methods and constructors.

In particular, given method initialisers, why support function declarations as well?

Also, are nested classes best bound to prototype properties? class A { class B {} ... }; a = new A; b = new a.B; ... is consistent along one dimension but it expresses something super-rare in the prototypal pattern used today, in my view unheard of.

An alternative to property initialisers only, one Dave and I misunderstood the strawman to be proposing: declarations, as opposed to initialisers, bound temporaries once in the lexical scope of the class body, for use by methods and other nested forms. In other words, implicitly private class static bindings. Was this considered?

The simplest idea would be initialisers only, and I know that created a commas-not-semicolons confusion, but that could be changed. Class bodies are neither exactly initialiser nor function bodies, but they might rather be initialiser-like with ; instead of , where a braced body does not terminate the element.

As a ClassElement, "x = y;" is just shorthand for "let x = y;". Likewise "static x = y;" is just shorthand for "static let x = y;".

Except let binds lexically in other declarations. This seems an aesthetic/philosophical objection -- we can make let mean what we like in class body context -- but it also smells bad to me. You?

Also, Gilad Bracha has shown many cool patterns in Newspeak leveraging the ability to inherit a default nested class but being able to override it by a like-named nested class in a subclass. Once you get used to it, it's actually much more natural and expressive than static nested classes.

I think I read Gilad's blog on that a while ago, I'll have to re-read. Again, though, there is nothing like this in idiomatic prototypal JS that I've seen.

# Brendan Eich (13 years ago)

On May 19, 2011, at 5:03 PM, Bob Nystrom wrote:

On Thu, May 19, 2011 at 3:08 PM, Brendan Eich <brendan at mozilla.com> wrote: class Point { x = 0; ... }

is a bit of a mystery.

Is it an assignment expression-statement? No, no statements as ClassElements.

Could it be creating a prototype property, as other unprefixed property initialisers such as get and set accessor declarations, and property initialiser extensions declaring prototype methods, do? Yes, that's the intent.

But, I object (mildly), it does not look like a property initialiser. It looks like an assignment expression-statement.

Agreed, completely. My first stab at this used var for declaring fields:

class Point { var x = 0; // x field on prototype }

Yes, I see your point now. Probably we should jump to thread off of my reply to Mark.

# Brendan Eich (13 years ago)

a few more thoughts/questions/distinctions.

On May 19, 2011, at 1:08 PM, Bob Nystrom wrote:

On Thu, May 19, 2011 at 11:47 AM, Brendan Eich <brendan at mozilla.com> wrote: Here's another question: how many property initializations could be done straight from the parameter of the same name?

Of the 88 fields I looked at, 10 were straight copies of a constructor argument. Another 4 are copies with an "if undefined then use this default instead" check.

??=, check (strawman:default_operator -- this is going for Harmony gold next week!).

That may be biased a bit low since most of the code I looked at is UI widget-like stuff.

Also, Closure library, right? Opinions vary on the style used there.

Not taking sides, just asking for you lurkers who use other libraries to do similar counting exercises with those libraries, if you all can spare the time. Thanks.

Code that's more data-model oriented would probably have more cases where a class is just wrapping a bunch of arguments.

Apart from the desire to declare per-instance property names, do we really need default values if the constructor author could write default parameter values?

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

That covers a different (though useful!) case. This would be helpful for the 4 cases above where a field is initialized to a constructor argument if given, or to a default value if not.

Hmm, parameter default values != the ??= operator, because while missing actual parameters are undefined, and ??= selects the right-hand side if the left-hand side evaluates to undefined, you can pass undefined followed by other actual parameters to trigger ??=-based defaulting instead of missing (trailing actual) parameter defaulting.

Are the 4 cases all such that the calls to the constructors leave off trailing arguments, or do they pass undefined? Or perhaps they pass other falsy default values, in which case ||= not ??= (and again not parameter default values) could help?

# Bob Nystrom (13 years ago)

On Thu, May 19, 2011 at 5:20 PM, Brendan Eich <brendan at mozilla.com> wrote:

Taken together, I wonder whether we aren't abusing Declaration to bind properties.

+1. I've been mostly working at this informally and haven't spent much time looking at the EBNF yet, but I'd expect a class body to have its own set of productions just for the small set of members you can define in a class (fields, getters, setters, methods, constants). We may want to expand that a bit (nested classes?), but I like the idea of it being its own set of declaration forms and not just "every declaration ES allows elsewhere".

Also, are nested classes best bound to prototype properties? class A { class

B {} ... }; a = new A; b = new a.B; ... is consistent along one dimension but it expresses something super-rare in the prototypal pattern used today, in my view unheard of.

This is a deeply fishy corner case. If you did:

class Outer { class Inner {} }

then the proposal implies Inner would indeed be a property on Outer.prototype, but my strong hunch is users would expect that to be on the ctor (Outer.Inner). To get that, you have to do:

class Outer { static class Inner {} }

An alternative to property initialisers only, one Dave and I misunderstood

the strawman to be proposing: declarations, as opposed to initialisers, bound temporaries once in the lexical scope of the class body, for use by methods and other nested forms. In other words, implicitly private class static bindings. Was this considered?

Mark's original proposal treated a class body like a regular lexical scope, which implies having local variables in it. I think the trouble was that it also implied allowing any arbitrary statement inside the class body, and that makes it really hard to have a nice syntax for member declarations (which are what the majority of a class body is). Allowing any statement in there stomps over so much syntactic space that there isn't a lot left for what we care about.

So the consensus we came up with was that a class body isn't a lexical scope and only allows member definitions. That makes it easy to have a nice syntax for those, and gives us breathing room in the future for adding new stuff to classes (like mixins). If you want a lexical scope your class members can access, you can just nest the whole class definition:

let MyClass; { let superSecret = 'shhh!';

MyClass = class { isSecret(maybe) { return maybe == superSecret; } } }

(There's probably a less awkward way to do that, but you get the idea.)

As a ClassElement, "x = y;" is just shorthand for "let x = y;". Likewise "static x = y;" is just shorthand for "static let x = y;".

Except let binds lexically in other declarations. This seems an aesthetic/philosophical objection -- we can make let mean what we like in class body context -- but it also smells bad to me. You?

Given the choice between let and var for this, I'd lean towards var (or a new keyword entirely), but I could probably be convinced either way.

# Bob Nystrom (13 years ago)

On Thu, May 19, 2011 at 5:40 PM, Brendan Eich <brendan at mozilla.com> wrote:

Of the 88 fields I looked at, 10 were straight copies of a constructor argument. Another 4 are copies with an "if undefined then use this default instead" check.

??=, check (strawman:default_operator -- this is going for Harmony gold next week!).

Yup, I like it. :)

Also, Closure library, right? Opinions vary on the style used there.

Yeah, code built on top of that, but not part of Closure itself. It would definitely be good to look at other JS too. My professional JS experience is all Closure-style so that likely gives me all sorts of weird biases.

Are the 4 cases all such that the calls to the constructors leave off trailing arguments, or do they pass undefined? Or perhaps they pass other falsy default values, in which case ||= not ??= (and again not parameter default values) could help?

For the most part, it's trailing values, but a couple were stuff like:

function SomeClass(opt_a, opt_b) { this.a = goog.isDef(opt_a) ? opt_a : SOME_DEFAULT; this.b = goog.isDef(opt_b) ? opt_b : SOME_DEFAULT; }

And there may be call sites like new SomeClass(undefined, "blah") I think most people try to minimize those cases, but they seem to crop up, especially as code accretes arguments over time.

# Brendan Eich (13 years ago)

On May 19, 2011, at 6:08 PM, Bob Nystrom wrote:

And there may be call sites like new SomeClass(undefined, "blah") I think most people try to minimize those cases, but they seem to crop up, especially as code accretes arguments over time.

Any use of null instead of undefined for same effect?

# Juan Ignacio Dopazo (13 years ago)

On Thu, May 19, 2011 at 9:40 PM, Brendan Eich <brendan at mozilla.com> wrote:

Not taking sides, just asking for you lurkers who use other libraries to do similar counting exercises with those libraries, if you all can spare the time. Thanks.

The reality is that in most cases libraries rarely use the constructor(x) { this.x = x } pattern because they tend to create an abstraction to define getters and setters through methods (usually get("prop") and set("prop", value), but sometimes using getProp() and setProp(value)) and use an object as a parameter instead of many parameters. Ie:

var widget = new Widget({ content: 'hello world', title: 'example' });

This is a very common pattern. Personally I always use it because for me it's always easier to remember parameter names than parameter order. Of course ES5's getters and setters change the situation a lot.

On a side note, there are a couple of aspects in the "object initializer extension" proposal that I liked a lot and which value I think is being underestimated. The class body similar to an object declaration with commas and colons make it very easy to refactor a class into an object and viceversa. This is not only useful for writing, but it also helps reading because it relates complex objects and classes, which is what we're nowadays doing with the most blogged about pattern in JS, the "module pattern", (function(){ return {} }()) and new function () {}.

Is a middle ground possible?

# Brendan Eich (13 years ago)

On May 19, 2011, at 6:31 PM, Juan Ignacio Dopazo wrote:

On Thu, May 19, 2011 at 9:40 PM, Brendan Eich <brendan at mozilla.com> wrote: Not taking sides, just asking for you lurkers who use other libraries to do similar counting exercises with those libraries, if you all can spare the time. Thanks.

The reality is that in most cases libraries rarely use the constructor(x) { this.x = x } pattern because they tend to create an abstraction to define getters and setters through methods (usually get("prop") and set("prop", value), but sometimes using getProp() and setProp(value)) and use an object as a parameter instead of many parameters. Ie:

var widget = new Widget({ content: 'hello world', title: 'example' });

This is a very common pattern. Personally I always use it because for me it's always easier to remember parameter names than parameter order. Of course ES5's getters and setters change the situation a lot.

It's common but I see other styles too, including fixed parameter lists. Good to hear, though -- thanks.

On a side note, there are a couple of aspects in the "object initializer extension" proposal that I liked a lot and which value I think is being underestimated. The class body similar to an object declaration with commas and colons make it very easy to refactor a class into an object and viceversa. This is not only useful for writing, but it also helps reading because it relates complex objects and classes, which is what we're nowadays doing with the most blogged about pattern in JS, the "module pattern", (function(){ return {} }()) and new function () {}.

Is a middle ground possible?

I tend not to think so (tertium non datur), but initialiser syntax really blows for class bodies when you start mixing in declarations. Declarations want to end with ; or } and never comma after }. So holding to initializer syntax only, and being disciplined about it, might work.

# Brendan Eich (13 years ago)

On May 19, 2011, at 6:34 PM, Brendan Eich wrote:

On May 19, 2011, at 6:31 PM, Juan Ignacio Dopazo wrote:

On a side note, there are a couple of aspects in the "object initializer extension" proposal that I liked a lot and which value I think is being underestimated. The class body similar to an object declaration with commas and colons make it very easy to refactor a class into an object and viceversa. This is not only useful for writing, but it also helps reading because it relates complex objects and classes, which is what we're nowadays doing with the most blogged about pattern in JS, the "module pattern", (function(){ return {} }()) and new function () {}.

Is a middle ground possible?

I tend not to think so (tertium non datur), but initialiser syntax really blows for class bodies when you start mixing in declarations. Declarations want to end with ; or } and never comma after }. So holding to initializer syntax only, and being disciplined about it, might work.

Of course, get/set and now method property initialisers do end in } and then comma (mandatory if not last property in the outer object initialiser, optional if last). That's a bit confusing if anyone is reading these as declarations (function declarations). In C and C++ you learn and avoid }; at end of function, but since empty top-level declarations are allowed (yay C) this doesn't hurt. In JS you can't leave extra commas after braced bodies in other contexts.

If we succeed in standardizing at least the expression-body part of arrow function syntax (which requires restricting label syntax and refactoring the AssignmentExpression-using non-expression-grammar a bit), then short methods and accessors in initialisers, and perhaps class bodies as you propose, won't have the }, eyesore / syntax trap.

I hope this makes sense,

# Brendan Eich (13 years ago)

On May 19, 2011, at 3:00 PM, Luke Hoban wrote:

  1. The conventional JS pattern is to place methods on the prototype, not the instance, and many felt that the main thing classes need to provide is a syntax to make this traditional semantics easier to express.

This is the big one.

Class syntax with class parameters instead of explicit constructors can still be defined to produce methods on the prototype, right?

Andreas' example:

class Point(x, y) { public function abs() { return Math.sqrt(xx, yy) } }

Can effectively desguar to:

function Point(x,y) { this.x = x; this.y = y; } Point.prototype.abs = function() { return Math.sqrt(this.xthis.x, this.ythis.y); }

Try a non-trivial constructor that wants instance properties x and y computed from parameters x0 and y0, and does not want properties named x and y.

We should not implicitly create instance properties from class parameter names. I did not take Andreas's message as proposing that.

But let's say the instance variables are declared via

class Point(x0, y0) { public x, y; ... // somehow set x and y in a constructor body public function abs() { return Math.sqrt(xx, yy) } }

Why should there be a non-local rewriting of x and y in function abs to this.x and this.y? Just because they were declared via public?

This is too implicit by far. We need the ability to write constructor bodies that initialize instance properties not necessarily named by constructor parameters. Yes, shorthands for the cliché, e.g.

class Point { constructor(this.x, this.y) {} ... }

with parameter default values allowed, are good and (I argue) necessary for competitive reasons. But the full constructor form, whatever it is, must enable parameters to be used freely to compute initial values of instance properties, and some instance properties can have parameter-independent initial values.

And in no case should rewriting or non-lexical scope implicitly come into play to rewrite names in methods to reference instance properties. Shorthands, again, are conceivable and possibly important for usability.

In your example, lexical scoping, the fundamental shared value that enabled Harmony, would have readers think x and y in abs refer to the parameters, not to the instance properties. The implicit default constructor copies the parameters to the instance properties, true -- but that's not evident from the syntax.

And since we need the ability to write non-default constructors with explicit property setting on the instance, I do not see how the rewriting of unqualified identifier expressions in method bodies can work -- not without mandating public x, y. But we do not want to mandate declaration of instance properties that the constructor (or any other code) might set.

Or, with private names, to a variant with the captured x and y stored as private fields on the instance. It's really just a syntax choice between explicit constructor functions and parameterized class definitions. There are many benefits to the latter, and many modern OO languages seem to be using this style effectively.

I don't think so. The example needs elaboration to allow for non-default constructor. The non-lexical scope objection applies. And JS is not C# ot any static language. There's a lot to like in many languages but OO does not entail implicit constructors and rewriting.

(I hope you don't mean that the instance (this) should be in the scope chain of each method, because that is non-prototypal and requires a method-clone per instance per prototype (class-declared without keyword prefix) method.)

# Brendan Eich (13 years ago)

On May 19, 2011, at 4:01 PM, Mark S. Miller wrote:

Most other OO languages don't have the this-confusion hazards that JavaScript suffers from.

To put things in a better light, most of those OO languages don't have methods as first class functions, either. (So there! :-P)

# Andreas Rossberg (13 years ago)

On 19 May 2011 15:36, Mark S. Miller <erights at google.com> wrote:

Hi Andreas, yes we have a long history of consider this shape, in fact much longer than the current shape. The final state of proposals along these lines is doku.php?id=strawman:classes_with_trait_composition&rev=1299750065.

I'll have a closer look, thanks for the pointer!

  1. Starting from scratch, there's no problem engineering a VM to make objects-as-closures efficient, especially given the semi-static analysis that class proposal was designed to enable. However, JS VM implementors are not starting from scratch. Fitting such a new optimization into existing heavily optimized engines was thought to be a hard sell. Especially since all major VM implementors would need to agree.

  2. The conventional JS pattern is to place methods on the prototype, not the instance, and many felt that the main thing classes need to provide is a syntax to make this traditional semantics easier to express. Another variation of your suggestion that Tom suggested is that you mix instance initialization and class/prototype initialization together in the class body. This obscures both time-of-execution and scope. Methods on the prototype do cannot have the constructor parameters in scope.

Like Luke I was wondering why a change of syntax should affect the semantics. But after seeing your reply pointing out the problem with `this', I see your point. Although it doesn't seem to specific to constructor parameters, but would apply to instance fields in general with the class-as-block approach. Still sad.

I agree that the ideal solution would be objects-as-closures. Personally, though, I could also live with requiring instance variables and constructor arguments always be accessed through `this', even in that syntactic approach. True, it somewhat obscures scoping -- but arguably, "public" or "private" declarations are not ordinary declarations, so it's not too hard to argue that they do not actually bind anything in the "current" scope. Same goes for constructor arguments vs ordinary function arguments.

Thanks,

# Andreas Rossberg (13 years ago)

On 19 May 2011 16:05, David Herman <dherman at mozilla.com> wrote:

Yes, we've talked about this. One of the issues I don't know how to resolve is if we want to allow the specification of class properties aka statics, then those need not to be in the scope of the constructor arguments, which ends up with very strange scoping behavior:

var x = "outer"    class C(x) {        static foo = x // "outer" -- whoa!    }

I'm not 100% up on the current thinking of the group that's been working on classes, and whether they are including statics in the design, but I think they are.

Oh, it wasn't clear to me that we really want to have static members. I may be biased here, but I always viewed static members as just a poor man's substitute for a proper module system. Fortunately, it looks like we will have a real one instead!

To be honest, I'm a bit worried that there will be a lot of semantic redundancy in the end. After adding modules + classes + static members (+ traits?), there would be at least three or four different, complicated constructs that evaluate to some kind of object, with significant functional overlap.

Thanks,

# Mark S. Miller (13 years ago)

Modules aren't generative.

sent from android On May 20, 2011 7:58 AM, "Andreas Rossberg" <rossberg at google.com> wrote:

On 19 May 2011 16:05, David Herman <dherman at mozilla.com> wrote:

Yes, we've talked about this. One of the issues I don't know how to

resolve is if we want to allow the specification of class properties aka statics, then those need not to be in the scope of the constructor arguments, which ends up with very strange scoping behavior:

var x = "outer" class C(x) { static foo = x // "outer" -- whoa! }

I'm not 100% up on the current thinking of the group that's been working

on classes, and whether they are including statics in the design, but I think they are.

# David Herman (13 years ago)

Oh, it wasn't clear to me that we really want to have static members. I may be biased here, but I always viewed static members as just a poor man's substitute for a proper module system. Fortunately, it looks like we will have a real one instead!

I'm sympathetic to that view, but statics also have precedent in JS, so I would bet people will continue to use them. I think it's not unreasonable to want a declarative way to create members of the constructor function object. That said, in the interest of parsimony, if we had to start cutting, statics would probably be one of the first I'd eliminate.

To be honest, I'm a bit worried that there will be a lot of semantic redundancy in the end. After adding modules + classes + static members (+ traits?), there would be at least three or four different, complicated constructs that evaluate to some kind of object, with significant functional overlap.

I think "modules are a construct that evaluates to an object" is the wrong way to think about them. Syntactic modules are a second-class construct that is not an expression. You can reflect on modules at runtime, and that reflection is provided as an object, but that's because almost all compound data structures in JS are objects. But I would advise against describing modules as a kind of object.

And I think an important aspect of classes is that they are providing a declarative convenience for doing things that people already do with objects in JS today.

So I'm not as worried about redundancy here.

# Bob Nystrom (13 years ago)

On Thu, May 19, 2011 at 6:13 PM, Brendan Eich <brendan at mozilla.com> wrote:

On May 19, 2011, at 6:08 PM, Bob Nystrom wrote:

And there may be call sites like new SomeClass(undefined, "blah") I think most people try to minimize those cases, but they seem to crop up, especially as code accretes arguments over time.

Any use of null instead of undefined for same effect?

If I recall, there are places where we do that but I think most of the code I've seen tries to use undefined.

# Andreas Rossberg (13 years ago)

On 20 May 2011 18:00, David Herman <dherman at mozilla.com> wrote:

I think "modules are a construct that evaluates to an object" is the wrong way to think about them. Syntactic modules are a second-class construct that is not an expression. You can reflect on modules at runtime, and that reflection is provided as an object, but that's because almost all compound data structures in JS are objects. But I would advise against describing modules as a kind of object.

And I think an important aspect of classes is that they are providing a declarative convenience for doing things that people already do with objects in JS today.

I see what you are saying, and yes, they are intended to serve a different purpose. But they still share a lot of semantic overlap. And I foresee that the overlap will increase overtime, as the language evolves.

Take just one specific example: there already is the proposal for extending modules with "module functions" (strawman:simple_module_functions) -- which makes a lot of sense, is straightforward, and I'm sure that people will demand something along these lines sooner or later. But for better or worse, modules now actually have become classes! Compare:

class Point { private x, y constructor(x0, y0) { x = x0; y = y0 } public function move(dx, dy) { x += dx; y += dy } public function abs() { return Math.sqrt(xx, yy) } }

let p = new Point(3, 4) p.abs()

with:

module Point(x0, y0) { let x = x0, y = y0 export function move(dx, dy) { x += dx; y += dy } export function abs() { return Math.sqrt(xx, yy) } }

let p = Point(3, 4) // assuming module functions are reflected into functions p.abs()

Almost the same effect, even though the underlying semantics differs somewhat. You can even express simple inheritance with import and export, depending on how general they will be in the end.

Obviously, there are aspects that you still cannot express with modules but can with classes, and vice versa. But my point is that at their core, they end up being pretty similar things. And their differences might eventually start looking rather accidental. I would feel better if we thought a bit harder about ways to utilize the commonalities before we grow the size of the language too quickly.

# Andreas Rossberg (13 years ago)

On 20 May 2011 15:42, Mark S. Miller <erights at google.com> wrote:

Modules aren't generative.

If you mean that you cannot create several objects from them, then yes, but see my reply to Dave.

However, I was primarily wondering about static members, which don't provide any generativity in that sense either.

# Mark S. Miller (13 years ago)

On Mon, May 23, 2011 at 2:16 PM, Andreas Rossberg <rossberg at google.com>wrote:

On 20 May 2011 18:00, David Herman <dherman at mozilla.com> wrote:

I think "modules are a construct that evaluates to an object" is the wrong way to think about them. Syntactic modules are a second-class construct that is not an expression. You can reflect on modules at runtime, and that reflection is provided as an object, but that's because almost all compound data structures in JS are objects. But I would advise against describing modules as a kind of object.

And I think an important aspect of classes is that they are providing a declarative convenience for doing things that people already do with objects in JS today.

I see what you are saying, and yes, they are intended to serve a different purpose. But they still share a lot of semantic overlap. And I foresee that the overlap will increase overtime, as the language evolves.

Take just one specific example: there already is the proposal for extending modules with "module functions" (strawman:simple_module_functions) -- which makes a lot of sense, is straightforward, and I'm sure that people will demand something along these lines sooner or later. But for better or worse, modules now actually have become classes! Compare:

class Point { private x, y constructor(x0, y0) { x = x0; y = y0 } public function move(dx, dy) { x += dx; y += dy } public function abs() { return Math.sqrt(xx, yy) } }

let p = new Point(3, 4) p.abs()

with:

module Point(x0, y0) { let x = x0, y = y0 export function move(dx, dy) { x += dx; y += dy } export function abs() { return Math.sqrt(xx, yy) } }

let p = Point(3, 4) // assuming module functions are reflected into functions p.abs()

Almost the same effect, even though the underlying semantics differs somewhat.

Regarding the scoping of private instance variables, the version with generative module functions is actually much better[1]. In fact, it's the same as the objects-as-closures pattern, or the earlier < doku.php?id=strawman:classes_with_trait_composition&rev=1299750065>

classes strawman, which is essentially a codification of objects-as-closures. Personally, I still like that earlier strawman better. But I gave up on it based on the difficulty of convincing implementors to implement it efficiently. Your's seems to have the same efficiency issue. Since you are now involved in JS implementation, let me ask regarding your efforts:

In the above example, do you anticipate an allocation per method per instance? Specifically, does each call to Point allocate a new instance of the point module (fine) exporting newly allocated move and abs closures (bad). Some codification of objects-as-closures only becomes a viable alternative to the current manual class pattern if it can avoid these extra allocations.

Putting methods on a shared prototype essentially treats the prototype as a vtable. This implementation path is messy, but is well trodden in current JS implementations. Avoiding extra allocations for objects-as-closures, whether sugared by your generative module pattern on my earlier classes strawman, would seem to require a new vtable mechanism not directly mapped onto prototype inheritance. Perhaps the "hidden classes" optimization already provides a context in which we can reuse implementation machinery between these two vtable-like mechanisms?

[1] The semantics of the "x" reference in your move and abs methods differs so violently from a lexical variable reference < esdiscuss/2011-May/014516> that we

must not make it appear to be a lexical variable reference. Starting with ES5, we have finally rescued lexical scoping from JavaScript's messy past. Let's not lose the ground we gained. Brevity is important, but scoping clarity is way more important than brevity.

You can even express simple inheritance with import and export, depending on how general they will be in the end.

Obviously, there are aspects that you still cannot express with modules but can with classes, and vice versa. But my point is that at their core, they end up being pretty similar things. And their differences might eventually start looking rather accidental. I would feel better if we thought a bit harder about ways to utilize the commonalities before we grow the size of the language too quickly.

Today is the day before the May meeting. If generative module functions could be grown to meet the needs of classes, I think I would prefer the outcome as compared to the current classes strawman. However, the open issues that would need to be explored to determine this vastly exceeds the time between now and then.

The May meeting is the close of the "additive phase" of designing ES-next. Following that, we hope for some consolidation and subtraction, among other activities (prototype implementations, web testing, spec writing, etc). Modules are already in. If classes get accepted in May, then I would consider it in bounds after May to grow modules slightly in order to remove classes completely. This would seem to be an excellent tradeoff. As I recall, you were planning to be at the July meeting? I think this would be a good focus topic for July.

# Mark S. Miller (13 years ago)

On Mon, May 23, 2011 at 7:22 AM, Andreas Rossberg <rossberg at google.com>wrote:

On 20 May 2011 15:42, Mark S. Miller <erights at google.com> wrote:

Modules aren't generative.

If you mean that you cannot create several objects from them, then yes, but see my reply to Dave.

However, I was primarily wondering about static members, which don't provide any generativity in that sense either.

Static members on classes are generative merely because classes are generative:

function makeFoo() {
  return class Foo {
    static x = Math.random();
    //...
  }
}

Your reply to Dave makes use of generative module functions. Yes, if we have generative module functions then my objection disappears. Modules would then also be generative.

# Mark S. Miller (13 years ago)

On Mon, May 23, 2011 at 10:42 AM, Mark S. Miller <erights at google.com> wrote:

On Mon, May 23, 2011 at 2:16 PM, Andreas Rossberg <rossberg at google.com>wrote:

On 20 May 2011 18:00, David Herman <dherman at mozilla.com> wrote:

I think "modules are a construct that evaluates to an object" is the wrong way to think about them. Syntactic modules are a second-class construct that is not an expression. You can reflect on modules at runtime, and that reflection is provided as an object, but that's because almost all compound data structures in JS are objects. But I would advise against describing modules as a kind of object.

And I think an important aspect of classes is that they are providing a declarative convenience for doing things that people already do with objects in JS today.

I see what you are saying, and yes, they are intended to serve a different purpose. But they still share a lot of semantic overlap. And I foresee that the overlap will increase overtime, as the language evolves.

Take just one specific example: there already is the proposal for extending modules with "module functions" (strawman:simple_module_functions) -- which makes a lot of sense, is straightforward, and I'm sure that people will demand something along these lines sooner or later. But for better or worse, modules now actually have become classes! Compare:

class Point { private x, y constructor(x0, y0) { x = x0; y = y0 } public function move(dx, dy) { x += dx; y += dy } public function abs() { return Math.sqrt(xx, yy) } }

let p = new Point(3, 4) p.abs()

with:

module Point(x0, y0) { let x = x0, y = y0 export function move(dx, dy) { x += dx; y += dy } export function abs() { return Math.sqrt(xx, yy) } }

let p = Point(3, 4) // assuming module functions are reflected into functions p.abs()

Almost the same effect, even though the underlying semantics differs somewhat.

Regarding the scoping of private instance variables, the version with generative module functions is actually much better[1]. In fact, it's the same as the objects-as-closures pattern, or the earlier < doku.php?id=strawman:classes_with_trait_composition&rev=1299750065> classes strawman, which is essentially a codification of objects-as-closures. Personally, I still like that earlier strawman better. But I gave up on it based on the difficulty of convincing implementors to implement it efficiently. Your's seems to have the same efficiency issue. Since you are now involved in JS implementation, let me ask regarding your efforts:

In the above example, do you anticipate an allocation per method per instance? Specifically, does each call to Point allocate a new instance of the point module (fine) exporting newly allocated move and abs closures (bad). Some codification of objects-as-closures only becomes a viable alternative to the current manual class pattern if it can avoid these extra allocations.

Putting methods on a shared prototype essentially treats the prototype as a vtable. This implementation path is messy, but is well trodden in current JS implementations. Avoiding extra allocations for objects-as-closures, whether sugared by your generative module pattern on my earlier classes strawman,

Should be

... your generative module pattern or my earlier classes strawman ...

# Brendan Eich (13 years ago)

On May 23, 2011, at 10:48 AM, Mark S. Miller wrote:

Static members on classes are generative merely because classes are generative:

function makeFoo() {
  return class Foo {
    static x = Math.random();
    //...
  }
}

Your reply to Dave makes use of generative module functions. Yes, if we have generative module functions then my objection disappears. Modules would then also be generative.

Not without the module-function head syntax, or we lose lexical scope with static errors for typos, though. The last issue is huge, and it does not affect only import M.*.

Possibly we have different undrestandings of module functions, but the base module system is static and second class. This should be unchanged by any extension, and extensions building on module system should not have "too different" semantics (I'm a broken record on this; also I agree it bites generators, wherefore the function*(){} minimal generator syntax idea. The syntax difference need not be huge,but it should be distinctive).

# Andreas Rossberg (13 years ago)

On 23 May 2011 19:42, Mark S. Miller <erights at google.com> wrote:

On Mon, May 23, 2011 at 2:16 PM, Andreas Rossberg <rossberg at google.com> wrote:

Compare:

class Point {    private x, y    constructor(x0, y0) { x = x0; y = y0 }    public function move(dx, dy) { x += dx; y += dy }    public function abs() { return Math.sqrt(xx, yy) }  }

let p = new Point(3, 4)  p.abs()

with:

module Point(x0, y0) {    let x = x0, y = y0    export function move(dx, dy) { x += dx; y += dy }    export function abs() { return Math.sqrt(xx, yy) }  }

let p = Point(3, 4)  // assuming module functions are reflected into functions  p.abs()

Almost the same effect, even though the underlying semantics differs somewhat.

Regarding the scoping of private instance variables, the version with generative module functions is actually much better[1]. In fact, it's the same as the objects-as-closures pattern, or the earlier doku.php?id=strawman:classes_with_trait_composition&rev=1299750065 classes strawman, which is essentially a codification of objects-as-closures.

Yes. I didn't want to stress that point, but it may be relevant, too, for better or worse.

In the above example, do you anticipate an allocation per method per instance? Specifically, does each call to Point allocate a new instance of the point module (fine) exporting newly allocated move and abs closures (bad). Some codification of objects-as-closures only becomes a viable alternative to the current manual class pattern if it can avoid these extra allocations.

Well, this was rather an observation than a proposal, so I didn't anticipate anything specific. But if the language grows in that direction, people might start using it that way, intended or not, whether we have classes as well, or not. So it potentially becomes an issue either way.

Putting methods on a shared prototype essentially treats the prototype as a vtable. This implementation path is messy, but is well trodden in current JS implementations. Avoiding extra allocations for objects-as-closures, whether sugared by your generative module pattern on my earlier classes strawman, would seem to require a new vtable mechanism not directly mapped onto prototype inheritance. Perhaps the "hidden classes" optimization already provides a context in which we can reuse implementation machinery between these two vtable-like mechanisms?

I'm afraid I don't know the "hidden classes" optimization. But speculating a bit, one implementation technique I could envision is the following. It amounts to moving the closure environment from individual functions to the module instance object:

  • Add the module's (internal) lexical environment as a hidden property Env to the module instance object.
  • Store (direct) function members as mere code objects, not closures.
  • Treat projection from a module M (which we can distinguish statically) specially. If we project a yet unclosed function:
    • if it's called right away, pass M.Env as the environment,
    • if it's not called, allocate a proper closure for it with environment M.Env,

Allocating the individual closure is effectively deferred to the rarer case where we extract a function without calling it immediately. This technique would only apply to functions that are direct members of the module, but that's usually the vast majority. For others, you'd close immediately, as you'd do now.

Note that this is merely an implementation trick, nothing the spec would or should have to worry about.

Example:

module M { let x = 2 // goes into M.Env export function f() { return x } }

let f = M.f // M is a module (statically), f a function (dynamically), so close over M.Env f() // 2

The May meeting is the close of the "additive phase" of designing ES-next. Following that, we hope for some consolidation and subtraction, among other activities (prototype implementations, web testing, spec writing, etc). Modules are already in. If classes get accepted in May, then I would consider it in bounds after May to grow modules slightly in order to remove classes completely. This would seem to be an excellent tradeoff. As I recall, you were planning to be at the July meeting? I think this would be a good focus topic for July.

OK, I will try to think this through in a bit more detail until then.

Thanks,

# Andreas Rossberg (13 years ago)

On 23 May 2011 19:48, Mark S. Miller <erights at google.com> wrote:

On Mon, May 23, 2011 at 7:22 AM, Andreas Rossberg <rossberg at google.com> wrote:

On 20 May 2011 15:42, Mark S. Miller <erights at google.com> wrote:

Modules aren't generative.

If you mean that you cannot create several objects from them, then yes, but see my reply to Dave.

However, I was primarily wondering about static members, which don't provide any generativity in that sense either.

Static members on classes are generative merely because classes are generative:     function makeFoo() {       return class Foo {         static x = Math.random();         //...       }     }

OK, now I see what you mean. However, I'd say that is not really a difference between the semantics of modules vs static class members per se, but simply a consequence of classes being first-class, while modules are restricted to second-class status. That, in turn, is merely a (well-advised) design choice of the current proposals, but nothing inherent to either modules or classes.

# Brendan Eich (13 years ago)

On May 24, 2011, at 6:20 AM, Andreas Rossberg wrote:

  • Treat projection from a module M (which we can distinguish statically) specially. If we project a yet unclosed function:
  • if it's called right away, pass M.Env as the environment,
  • if it's not called, allocate a proper closure for it with environment M.Env,

This is already done by SpiderMonkey and I believe other engines, with function expressions in certain contexts.

Not to rain on the module function parade (I'm leaning away from classes, in search of simpler solutions that compose well, so I have a soft spot for it), but reifying the module as an object with its exports as properties means those properties are non-configurable and non-writable. That goes against the classes-as-sugar-for-prototypal-objects goal. The -prototypal part is missing too.

# Gavin Barraclough (13 years ago)

Brendan,

Out of interest was a C++/Java style constructor considered, with the constructor name matching the class name?

# Bob Nystrom (13 years ago)

It was. The main problem that comes to mind is that it doesn't work with anonymous classes. Even with named classes, I find it can be tedious and repetitive, especially with long class names.

Javascript's current class-as-constructor syntax is nicely terse and doesn't require stuttering the "class" name. I think the goal with the class proposal was to aim for that level of syntactic lightness.

# Brendan Eich (13 years ago)

On Jun 7, 2011, at 12:42 PM, Gavin Barraclough wrote:

Brendan,

Out of interest was a C++/Java style constructor considered, with the constructor name matching the class name?

I'm not sure -- I wasn't in on the earlier design sessions among the champions. I know "new" was used, but if we are sugaring prototypal inheritance, we need something to create "constructor" in the prototype (which is where method definitions written directly in the class body bind their names).

Having some backstage magic wire up "constructor" is possible but then you have two names for the constructor. That is a wart.

The bigger issue is that doing this preempts use of "new" as a prototype property name (allowed by ES5).

So to keep things simple, express the prototypal machinery already in the language directly and explicitly, and avoid preempting "new", we've settled on "constructor".

The bigger issues with classes to close down include private instance variable syntax and semantics, and static method s&s. Some followup work is already under way here. We need to get this solid by the July TC39 meeting.

# Gavin Barraclough (13 years ago)

Cheers Brendan. I must say, I really like how the class sugar is coming together. It seems that more closely matching a familiar syntax might make things a little more approachable for existing OO programmers, but I certainly see the elegance in using "constructor" directly.

thanks, G.

# Gavin Barraclough (13 years ago)

On Jun 7, 2011, at 12:57 PM, Bob Nystrom wrote:

The main problem that comes to mind is that it doesn't work with anonymous classes.

Ah, good point, I hadn't considered anonymous classes. Clearly we could solve this with a solution specific to these cases (say, "class(){ /.../ }" for anonymous class constructors?), but having mismatching syntax in these cases would be a little unfortunate.

thanks, G.

# Bob Nystrom (13 years ago)

On Tue, Jun 7, 2011 at 1:19 PM, Gavin Barraclough <barraclough at apple.com>wrote:

Ah, good point, I hadn't considered anonymous classes. Clearly we could solve this with a solution specific to these cases (say, "class(){ /.../ }" for anonymous class constructors?), but having mismatching syntax in these cases would be a little unfortunate.

That might work, but has the same problem as "new" I believe, where "class" is a valid property name. It also might be a bit confusing if we later add support for nested classes.

For what it's worth, I find "constructor" painfully long, but it seems to be the best solution. I definitely prefer using a single standard token instead of the class name. I hate having to type the same thing twice, especially if I'm writing a class WithSomePainfullyLongNameWhichSadlyDoesOccurInPractice.

Just for kicks, here's an even more radical idea: no name at all:

class Monkey { () { log("I am the monkey's constructor."); } }

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

It's delightfully terse, but also completely foreign and non-obvious. I'm 90% certain this is a terrible idea.