Sep 30 meeting notes

# Waldemar Horwat (15 years ago)

Here are my raw notes for today's meeting.

Waldemar

Excellent Japanese ISO comments!

Discussion of the comment on 7.9.1:

BreakStatement: break [no LineTerminator here] Identifier-opt ;

means:

BreakStatement: break [no LineTerminator here] ; break [no LineTerminator here] Identifier ;

which then means that code like:

break ;

is rejected. This is a bug in the ES5 spec. In the prehistory of ES3 we had intended the following:

BreakStatement: break ; break [no LineTerminator here] Identifier ;

but we over-abbreviated. Waldemar proposed fixing the spec to the two-line form above for break and continue, and an analogous fix for return. Consensus to fix it if we can intercept the draft disposition of comments document before it arrives at ISO.

Firefox conforms to the corrected grammar. Safari does something unusual: It allows a line terminator after the break but considers a semicolon after the line terminator to be an empty statement. This shows up if one writes:

if (...) break ; else ...

which is an error in Safari but not Firefox. We hope Safari will fix this tiny corner case.

If a ballot resolution meeting is needed, Waldemar will represent TC39. Allen can't do it because he can't have dual roles in this process, also being the editor of the ISO standard.

Test262: Microsoft executed the ECMA contributor agreement. Google hasn't yet but intends to. Allen would like to publicize the site at some point as an official place for ES tests. Several of us want to make sure that, if it's publicized, this is perceived as a work-in-progress, not as something that "certifies", "validates", ensures "conformance", etc. of implementations.

MarkM: ES5 tests are currently too wimpy. The pass rate is too high for implementations we know not to be ES5-conformant.

Fear is that journalists will look at a "98.72% conformance" grand total score and use it as a figure of merit. There will be pressure for vendors not to put up new conformance tests that they themselves fail.

MarkM and Waldemar: Please delete the grand total score line.

John Neumann: Concern about naming vendors in test results (as opposed to anonymous "Vendor 1", "Vendor 2", ...) on an ECMA-related site.

Internationalization effort:

Google and Microsoft are interested in participating. Maybe Mozilla.

Should the internationalization library standard be in lockstep with ES-Harmony, targeted for 2013? No need to wait for Harmony. Allen thinks that 2012 is realistic for a separate internationalization library standard. Waldemar: We can decide later whether it's best to fold the internationalization library into Harmony or leave it separate. If it's separate, we'll need to face issues with Harmony security, multiple global objects, etc. that may need to be backported to the internationalization library.

First internationalization meeting in November. We'll share the es-discuss mailing list.

Next meeting: Nov 16 (internationalization), 17, 18

MarkM and Sam Ruby: Interested in standardizing a debugging API. There is already some prior work, such as IBM's JSDT.

A number of others are interested in following the effort on es-discuss.

Classes as sugar:

Should public fields be enumerable? Yes, so the object can be serialized via JSON. Also, when traditionally creating objects as records, the fields will be enumerable.

Waldemar: Traits and classes-as-sugar proposals seem remarkably similar. Waldemar: How does one do class-private in the current traits proposal? MarkM: You can't. Waldemar: Concerned about easily being able to define high-integrity abstractions using whatever we use for classes or traits. Don't want to be able to sneak in something that looks like a ComplexNumber to some consumer but then spontaneously changes value after being validated.

We'll need to work out the corner cases of "this" usage in both proposals. We'll also need to address what happens when we throw out of a class block but squirreled away the value of "this" in a global variable. Probably no big deal.

Having the syntactic form "public foo = ..." to create a publicly readable, privately writable field is confusing.

Open discussion about interaction between traits and prototype chain. Traits follow prototype chain when used with Object.create; they are prebound when used with Trait.create. Debate about whether the common case is promiscuous object, nonpromiscuous objects, or both.

Dave: Somewhat prefer the Object.create approach.

MarkM: Make it convenient to create defensible objects.

Making traits high-integrity is inconvenient:

trait class PointTrait(x, y) { function getX() {return x;} => { getX: getX, ... ... getX ... // High-integrity usage ... this.getX ... // User overridable usage } }

There's no way to do the high-integrity usage on another (non-this) instance of a trait.

Schism within group about goals: "high integrity" vs. "supporting the things that people are already writing with better syntax" vs. maybe possible to get both

MarkM: In the world of mashups, new compositions come up on users' computers that developers did not test ahead of time. Need to ensure that components are robust/reliable enough individually so that the compositions will just work without the need for testing.

PostMessage security alternative: Very hard to program due to asynchrony at all inter-module boundaries.

Object initializer syntax: Angle brackets useful for disambiguating things like: var arrayLike = [ <prototype: obj>, "element0", "element1" ]; Per wiki, this is has [[class]] Object, not Array. Debate about whether it should be Array or whether we should allow <class: expr>.

Grammar is ambiguous because it allows the > operator in the

expression inside <prototype: expr>.

Wiki proposal doesn't allow null prototypes. Agreed to allow them.

Debate about whether the closing > should be followed by a comma.

What is the "final" metaproperty? It's "nonextensible" with a confusing name. Groans -- fools people into thinking that the object can't be used as a prototype.

Objections to making "const" fields automatically nonenumerable. This tying of two independent concepts makes it hard to serialize via JSON, etc.

Private properties are controversial. Do they pertain to just the instance being constructed, or can they be applied to foreign instances?

Waldemar, MarkM: If sole-instance, they're better expressed via closures (with an argument about implementation speed). If they're not sole-instance then within the context of "private privateVar", the expression "a.privateVar = 3" will do something different from a["privateVar"]. Instead, it will create a hidden property on a and give it the value3.

Allen's "static" proposal: Algol 60's "own" variables. At what point does a static initializer get evaluated? Function expression: at the time that the function itself is created. Function declaration: we'll have to think about it.

Waldemar: Not clear there is a right answer to when a static initializer gets evaluated for a function declaration. The obvious choices are either at the beginning of the next-outer function or just before the inner function declaration would have been executed. Each choice is wrong for one of the statics s1 or s2 below:

function Outer(x) { // Initialize s1 and s2 here? Inner2(); let y = ... x ...;

// Initialize s1 here? function Inner1(z) { static s1 = y; ... s1 ... }

// Initialize s2 here? function Inner2(z) { static s2 = x; ... s2 ... } }

Of course, we should also support "static static static" to go up three function levels.

# Waldemar Horwat (14 years ago)

My rough notes for today's meeting.

Waldemar

Test262.ecmascript.org presentation Allen: There will be substantial changes to section numbering in ES6. Concerned about too much dependence on section numbers in Test262. However, there exist tools to rename Test262 files with new section numbers.

Debate sparked by test262's requirement that defineProperty throw if it can't create the property -- that's an invariant that even host objects are not allowed to violate. We can't test all possible property names and objects, so the authors of the test have chosen ones which some implementations are known to violate. Is this kosher? Yes -- this is a normative invariant in the spec, even for implementation extensions. Discussion over refactoring of such tests to parametrize them over the property names known to violate the spec. This way the tests can include different implementations' suspicious properties. Debate over whether errors the test finds should be labeled as DOM errors or ECMAScript errors.

Test262 Technical Report will be submitted to the December GA. It must be approved and sent to ECMA members by October 8th to make it to the December GA.

Discussion about updating the ECMA TC39 web page.

ParallelArray demo. Currently the implementation is very hacky (i.e. works mainly just for this demo), but shows great promise. Discussion: How do we express arithmetic on number types other than IEEE double? This is a different problem from typed arrays in that we want to actually compute using other types (int8, float32, etc.) rather than just store using those types and convert to doubles for computation.

Allen's presentation on current state of the spec. Question: We have an opportunity to make function declarations create immutable bindings if they occur inside blocks. Answer: Consensus on making them consistent with function declarations outside blocks. Either we'd make all function bindings mutable or all immutable.

Discussion about object literal attribute modifiers. Objections to using := for immutable properties (intuition is opposite from that of Pascal). Disagreement over whether immutable properties should be nonenumerable -- some are the same in every instance, some are immutable but have different values in different instances. No consensus on any concise way of marking properties nonenumerable. DaveH: Prefer to play it conservative and do nothing rather than introduce a funky new syntax.

Evaluated property names: { [foo]:1, get1+2{}, 'prefix'+i++{} } Waldemar: Concerned that these look too much like array literals or an array index expression on an array named "get", which is not a reserved word. These can produce property name collisions at run time. What should happen? A. Rely on DefineProperty semantics, which sometimes allow rebinding of properties B. Throw In choice A we'd then have to allow collisions in literal property names as well. Leaning towards choice B.

Method form is not a constructor. Why? To be consistent with built-ins. Hotly debated whether we should be consistent here (and consistent with what -- with built-ins, with bind, or with standalone functions?) and whether the extra constructors are useful.

super wiring: If 'super' is nested inside one or more functions inside an object literal, what does 'super' bind to? { f:(0, function(){... super ...}), g:function(){... super ...}, h(){... super ...}, k:... super ... } Another hot debate with no convergence. If it doesn't bind to anything, should super: A. Be a compile-time syntax error? B. Return null? C. Return an empty object? D. Throw an error when containing function is entered? E. Throw an error when evaluated? Not resolved. Allen's proposal currently has choice C if super is used in a property lookup and the value of 'this' if super is not used in a property lookup. Debate over the meaning of: function returnUndefined() { return super.foo; } What about function returnUndefined() { super.foo = 42; } Error because there is no object.

What about assignments to super.foo in general, when there is a super? Allen: These behave the same as assignments to this.foo Waldemar: These should either do the 'super' thing or be an error. Silently changing the meaning to not do 'super' lookup is gratuitously unsymmetric.

Commas optional after method/getter/setter definitions. These currently always end with }. Note that an equivalent member definition that initializes the member to a function does not make the comma optional (and can't because of things like: { g:function(){...} [expr] ... } where the [expr] is an array lookup on the function literal.

Debate over whether method properties should be writable and configurable or not. Brendan: Use # to mark nonwritable/nonconfigurable properties. Waldemar: Want to have easy ways to specify both writable and non-writable nonconfigurable properties. MarkM: Nonconfigurable writable properties don't make sense because an adversary can freeze them. Waldemar: That's a design bug we have to live with, but they're still useful in the common case. Brendan: Accessors might solve this. Waldemar: Accessors are too wordy and would also rely on private properties here. Alex: Common accessor pattern of intercepting writes but passing reads through is too wordy and could use better support.

Waldemar: For methods, use # to mean nonconfigurable/nonwritable. For value properties, use # to mean nonconfigurable/writable. For value properties, use const to mean nonconfigurable/nonwritable. None of these prefixes have any effect on enumerability.

Alternate proposal: use # in front of entire object to freeze (MarkM) or seal (Waldemar) object.

Reached consensus on the following # proposal:

before an object literal seals the object

before a propoerty makes that property nonconfigurable and nonwritable.

All method properties are nonenumerable. All value properties are enumerable.

{x} desugars into {x:x} in the proposal. DaveH: Would prefer to turn that into {x:void 0}; DaveH withdrew proposal, but now Waldemar wants to do that, in order to be able to define objects that contain uninitialized instance variables x, y, z, length: #{x, y, z, length, ...} without capturing the values of x, y, z, length from the outer environment.

Allen's "class" definition pattern: DaveH: Too imperative for what should be a declarative construct Brendan: Doesn't substitute for classes MarkM: How would you freeze everything? Getting the .prototype. and .constructor. sections out of order or omitting one would lead to weird errors.

DaveH: Dislike the use of .{ for object extension. Like the idea in principle. It's not enough to say "this is the semantics we want, pending finding the right syntax". Some like the syntax. Debate. About as many syntax variants offered as there are people in the room.

# Waldemar Horwat (14 years ago)

Rough notes from today's meeting.

Waldemar

Classes: What do you get when you access an instance property before the directive defining it has been executed? A. Throw (just as in temporal dead zone for let/const variables) B. Get undefined C. Property does not exist yet on the object -- prototype shows through

Waldemar objects to B because it would allow observers to see const properties get mutated and is future-hostile for guards (a guard expression would not have even been evaluated at the time of the property access).

Allen's idea: Separate property definitions from any code that refers to "this" using a barrier statement that deems that the object has been initialized. Works for single-level classes but not for inheritance, as the superclass could have leaked "this". A suggestion was made to run all property definitions in all classes in the hierarchy before running imperative initializations in reverse hierarchy order. Waldemar doesn't know of any major language that does initializaiton backwards like this; the problem is that derived classes want to refer to the already initialized base class state so that they can initialize themselves.

Explored a different order: doing definitions in reverse hierarchy order and imperative initialization in hierarchy order.

At what point does a class instance acquire trademarks? When the constructor returns. But then what about constructors that create a trademarked object and then put them into a registry of all objects with that trademark? The constructor can't register the object because it's not trademarked yet before the constructor returns. Yet another kind of two-phase initialization?

Discussion of proto. All implementations currently disallow using it to mutate the prototype chain of nonextensible objects.

Should we standardize proto in Annex B? MarkM + a few others: Yes Waldemar, Doug: No

MarkM: Emphasizes that we can't allow one to add new private properties to nonextensible objects. Allowing that would create a hidden communication channel between two frozen entities given access to the same frozen object.

Back to throw/undefined/no-property debate.

Brendan: Fixing the const case (preventing observation of mutation) will also fix the guard case.

Exported bindings from modules are also reflectible as properties. If these are accessed before they execute (i.e. in the dynamic dead zone), they throw.

DaveH: Modules and classes are different things.

MarkM: Only have const classes? Others: Only have non-const classes?

DaveH: Currently have nothing exactly like a const field on an object. MarkM: Could specify it in terms of proxies.

DaveH: Proposed read barrier on const fields; no read barrier on let fields. Luke: Still worried about performance of dynamic field access. MarkM: Dubious about performance issue because the same case already arises for accessors.

Allen: We'd need to define a new instance member state: not yet initialized. We'd use this for reflecting on module instances as well.

DaveH: Not dogmatic about classes as sugar. OK to extend semantics in cases where the current ones are inadequate.

Brendan's summary: class Widget { constructor(a, b) { public const k = a*b const c = a+b ... } }

Similarities between const properties and lexical variables:

  • Initialize-only
  • Temporal dead zone Differences:
  • property vs. lexical binding (ignoring reflection on modules)
  • pedagogy

Went off on a tangent to discuss a class idea:

class Point(x, y) { getX() {return x} // Instance method because it refers to x getY() {return y} // Instance method because it refers to y getInstanceX() {return this.x} // prototype method getInstanceY() {return this.y} // prototype method foo() {return getX();} // Doesn't work -- need to use this.getX() public x = x, y = y; // Instance variables ... constructor code here ... alert(this.x); public m = function() {return x+y} } No way to factor a function into two functions without indirecting via "this" as in the foo line above. Adding a reference to x to a function changes where it lives.

Wondering why we're discussing this (which we've previously discussed) instead of discussing const and the dead zone.

DaveH: Don't go around throwing vetoes. We should not be finding the least objectionable thing.

Trying to understand Oliver's objections to the current class proposal.

Brendan's update on post-es.next paren-free. Luke: concerned about gratuitously having two different ways of doing the same thing. Brendan: Safe; omitting braces and parentheses will just yield a syntax error. Waldemar: Opportunities for mischief when combined with semicolon insertion: Start with ES5 code:

if (a + b) (x.y)() z++

Now (erroneously) convert it to paren-free:

if a + b (x.y)() z++

This doesn't give a syntax error; it silently changes the meaning of the program.

Block-lambdas: Now the |'s are required (syntax doesn't work if they're not).

# Brendan Eich (14 years ago)

On Sep 27, 2011, at 4:06 PM, Waldemar Horwat wrote:

Trying to understand Oliver's objections to the current class proposal.

Oliver objects, as do others, to "punning" (my word) declarative syntax to define object properties, and mixing static and dynamic influences. He specifically cited the location of public exportable-definitions in the constructor body, instead of in the class body.

We then discussed how putting public x,y; for a class Point in the class body requires separate assignments in the constructor body. Without type annotations (guards) the public declaration is easy to leave out -- it seems optional or unnecessary. And in the case of const (public const K) there's no way to initialize with a constructor parameter or other per-construction state. Anyway, this was the counter-argument, heard before.

Oliver, anything to add?

# Bob Nystrom (14 years ago)

On Tue, Sep 27, 2011 at 10:30 PM, Brendan Eich <brendan at mozilla.com> wrote:

On Sep 27, 2011, at 4:06 PM, Waldemar Horwat wrote:

Trying to understand Oliver's objections to the current class proposal.

Oliver objects, as do others, to "punning" (my word) declarative syntax to define object properties, and mixing static and dynamic influences. He specifically cited the location of public exportable-definitions in the constructor body, instead of in the class body.

We then discussed how putting public x,y; for a class Point in the class body requires separate assignments in the constructor body.

At some point, didn't we discuss borrowing a bit from CoffeeScript and supporting this shorthand:

class Point { public x, y; constructor(this.x, this.y) {} // <-- assigns args to instance fields }

If we were feeling crazy, we could go further (similar to Scala) and allow:

class Point { constructor(public x, public y) {} }

That gives you a declarative form, constructor argument, and initialization all in one place.

# Brendan Eich (14 years ago)

On Sep 28, 2011, at 11:07 AM, Bob Nystrom wrote:

On Tue, Sep 27, 2011 at 10:30 PM, Brendan Eich <brendan at mozilla.com> wrote: On Sep 27, 2011, at 4:06 PM, Waldemar Horwat wrote:

Trying to understand Oliver's objections to the current class proposal.

Oliver objects, as do others, to "punning" (my word) declarative syntax to define object properties, and mixing static and dynamic influences. He specifically cited the location of public exportable-definitions in the constructor body, instead of in the class body.

We then discussed how putting public x,y; for a class Point in the class body requires separate assignments in the constructor body.

At some point, didn't we discuss borrowing a bit from CoffeeScript and supporting this shorthand:

class Point { public x, y; constructor(this.x, this.y) {} // <-- assigns args to instance fields }

If we were feeling crazy, we could go further (similar to Scala) and allow:

class Point { constructor(public x, public y) {} }

That gives you a declarative form, constructor argument, and initialization all in one place.

+∞

When are your edits to the proposal appearing? :-|

# Oliver Hunt (14 years ago)

On Sep 27, 2011, at 10:30 PM, Brendan Eich wrote:

On Sep 27, 2011, at 4:06 PM, Waldemar Horwat wrote:

Trying to understand Oliver's objections to the current class proposal.

Oliver objects, as do others, to "punning" (my word) declarative syntax to define object properties, and mixing static and dynamic influences. He specifically cited the location of public exportable-definitions in the constructor body, instead of in the class body.

We then discussed how putting public x,y; for a class Point in the class body requires separate assignments in the constructor body. Without type annotations (guards) the public declaration is easy to leave out -- it seems optional or unnecessary. And in the case of const (public const K) there's no way to initialize with a constructor parameter or other per-construction state. Anyway, this was the counter-argument, heard before.

Oliver, anything to add?

I feel we're trying to hard to design a relatively common language feature without actually deciding what we want the fundamental semantics to be.

The basic branching point for class in most languages is how properties are added in an instance of a class -- methods seem to be used as the universal "this is a class" concept and so dynamically and statically typed languages have both moved to a declarative model for methods.

So the issue is whether or not we want to have declarative fields on our objects or not. The general impression I have got from the committee is that we favour a declarative syntax for fields, but we don't want to separative the declarative logic from the action of code. To me the various syntaxes being thrown around yesterday feel like rather than making a hard decision we're trying to compromise, and our approach to compromising is to average the syntaxes of static and dynamic classes.

This produces a language with hard to understand semantics, as we demonstrated by essentially spending an entire day arguing over the basic semantics of properties -- let alone any of the edge cases.

I would be happy if we went for a python-esque class model where methods are entirely declarative and properties are entirely dynamic, or a more locked down class model (a dynamically typed version of the common C++/Java/CLR/etc class system) so the set of fields is known and guaranteed -- I really don't favour one over the other, but the result of that decision directly effects what we should be attempting to do in syntax land.

Given our apparent desire to have a declarative syntax i don't see why something akin to:

class Foo { var bar; // default value -> undefined var wibble = 5; // initialised fields desugar to statements at the head of the constructor const wiffle; // read barrier on uninitialised

function someMethod(..) {
     this.bar = foo; // could have a short hand for this.property
}

constructor (bar, wibble) {
    // extra typing due to the removal of horrible punning
    // but "public bar = bar" or whatever that syntax was is longer
    // than this.bar
    this.bar = bar;
    this.wibble = wibble;
}

}

function's get shoved on to the prototype chain, and fields go on the instance, inheritance follows reasonably sensibly from that. Remove the declarative field definitions and you have essentially got python's class model, which seems to be well accepted by a wide array of people, otherwise the question becomes one of how much you want to lock down the resultant object shape.

# Russell Leggett (14 years ago)

On Wed, Sep 28, 2011 at 10:40 PM, Oliver Hunt <oliver at apple.com> wrote:

Given our apparent desire to have a declarative syntax i don't see why something akin to:

class Foo { var bar; // default value -> undefined var wibble = 5; // initialised fields desugar to statements at the head of the constructor const wiffle; // read barrier on uninitialised

function someMethod(..) { this.bar = foo; // could have a short hand for this.property }

constructor (bar, wibble) { // extra typing due to the removal of horrible punning // but "public bar = bar" or whatever that syntax was is longer // than this.bar this.bar = bar; this.wibble = wibble; } }

function's get shoved on to the prototype chain, and fields go on the instance, inheritance follows reasonably sensibly from that. Remove the declarative field definitions and you have essentially got python's class model, which seems to be well accepted by a wide array of people, otherwise the question becomes one of how much you want to lock down the resultant object shape.

--Oliver

Hi, just a voice from the peanut gallery - of the proposals going on right now, I really like this proposal a lot. Perhaps it is just my experience with Java, but this feels like an intuitive class syntax that also stays feeling like JavaScript. I like that it doesn't introduce a new keyword "public" which I especially didn't like because there was no "private" keyword.

In to private fields/methods in classes, I did also have a thought that perhaps you could use proxies to provide a peephole view of a class instance. Basically, the thought being when you say "new Foo()" it creates a new instance, then wraps it in a proxy exposing only the public fields/methods and returns that. Internally, the methods can still access the private (unexposed) members, but from the outside, with access only to the proxy, they would be hidden. I think this would avoid the need for the private key feature that has also been proposed.

Thanks, Russell Leggett

# Brendan Eich (14 years ago)

On Sep 29, 2011, at 3:20 PM, Russell Leggett wrote:

On Wed, Sep 28, 2011 at 10:40 PM, Oliver Hunt <oliver at apple.com> wrote:

Given our apparent desire to have a declarative syntax i don't see why something akin to:

class Foo { var bar; // default value -> undefined var wibble = 5; // initialised fields desugar to statements at the head of the constructor const wiffle; // read barrier on uninitialised

function someMethod(..) { this.bar = foo; // could have a short hand for this.property }

constructor (bar, wibble) { // extra typing due to the removal of horrible punning // but "public bar = bar" or whatever that syntax was is longer // than this.bar this.bar = bar; this.wibble = wibble; } }

function's get shoved on to the prototype chain, and fields go on the instance, inheritance follows reasonably sensibly from that. Remove the declarative field definitions and you have essentially got python's class model, which seems to be well accepted by a wide array of people, otherwise the question becomes one of how much you want to lock down the resultant object shape.

--Oliver

Hi, just a voice from the peanut gallery - of the proposals going on right now, I really like this proposal a lot. Perhaps it is just my experience with Java, but this feels like an intuitive class syntax that also stays feeling like JavaScript. I like that it doesn't introduce a new keyword "public" which I especially didn't like because there was no "private" keyword.

This syntax is awfully familiar from original-JS2/ES4 (Waldemar's 1999-2003 effort) and the 2006-2008-era ES4.

There are some on the committee who object to 'var' in particular; others object to using scoped binding forms to define properties in (default mutable) objects. We can go around this block again, though.

In to private fields/methods in classes, I did also have a thought that perhaps you could use proxies to provide a peephole view of a class instance.

Proxies are too heavyweight. We have private name objects:

harmony:private_name_objects

which can be implemented very cheaply, and our current consensus for private in classes is that you would bind module- or block-scoped consts to private names, one per class-private instance variable, and then use them via this[priv] = initialValue, Object.create, Object.defineProperty, etc.

# Oliver Hunt (14 years ago)

On Sep 29, 2011, at 7:52 AM, Brendan Eich wrote:

On Sep 29, 2011, at 3:20 PM, Russell Leggett wrote:

On Wed, Sep 28, 2011 at 10:40 PM, Oliver Hunt <oliver at apple.com> wrote:

Given our apparent desire to have a declarative syntax i don't see why something akin to:

Hi, just a voice from the peanut gallery - of the proposals going on right now, I really like this proposal a lot. Perhaps it is just my experience with Java, but this feels like an intuitive class syntax that also stays feeling like JavaScript. I like that it doesn't introduce a new keyword "public" which I especially didn't like because there was no "private" keyword.

This syntax is awfully familiar from original-JS2/ES4 (Waldemar's 1999-2003 effort) and the 2006-2008-era ES4.

There are some on the committee who object to 'var' in particular; others object to using scoped binding forms to define properties in (default mutable) objects. We can go around this block again, though.

The exact token isn't overly important, it could be '@' if we wanted to go down the path of perl and ruby and use random symbols in place of words. I would prefer var, because that makes sense to me -- just like the reuse of function for method decls. Python has demonstrated that people don't have a problem with the same keyword producing functions vs methods on classes (see def).

That said I don't now what you mean by "scoped binding forms" so I can't comment on the latter part of your reply.

In to private fields/methods in classes, I did also have a thought that perhaps you could use proxies to provide a peephole view of a class instance.

Proxies are too heavyweight. We have private name objects:

Yeah, I'm not too interested in Proxies as being something to use (or encourage in anyway) for common objects.

# Allen Wirfs-Brock (14 years ago)

On Sep 28, 2011, at 7:40 PM, Oliver Hunt wrote:

class Foo { var bar; // default value -> undefined var wibble = 5; // initialised fields desugar to statements at the head of the constructor const wiffle; // read barrier on uninitialised

But note that such read barriers must be part of every property access including simple method invocations because the instance can escape from the constructor before it is initialized.

function someMethod(..) { this.bar = foo; // could have a short hand for this.property }

constructor (bar, wibble) { // extra typing due to the removal of horrible punning // but "public bar = bar" or whatever that syntax was is longer // than this.bar theWiderWorld(this); //this escapes from constructor before wibble is initialized

   this.bar = bar;
   this.wibble = wibble;

} }

function elsewhere(obj,key) { var z = obj[key]; //needs read barrier, object might be an incomplete Foo instance and key might be 'wibble' ... }

To accommodate this, each property would seem to need an additional [[Uninitialized]] "attribute" (it could be represented as a distinguished value) which would have to be checked as part of every property access.

You could avoid this simply my not having such "const" declarations and leaving it to the class author to use Object.defineProperty to set the [[Writable]] attribute of such properties to false.

Alternatively, you could just not worry that a const property could escape into the wild in a writable state and the value undefined.

Presumably the job of the constructor is to establish invariants of the constructed instances. Some of those invariants may involve complex relationships among property values and between the new object and other objects. There are many ways that an object can escape from a constructor before these invariants are fully established. Why is the "const" invariant any more important than other invariants that we aren't attempting to guarantee?

# Brendan Eich (14 years ago)

On Sep 29, 2011, at 4:47 PM, Oliver Hunt wrote:

There are some on the committee who object to 'var' in particular; others object to using scoped binding forms to define properties in (default mutable) objects. We can go around this block again, though.

The exact token isn't overly important, it could be '@' if we wanted to go down the path of perl and ruby and use random symbols in place of words. I would prefer var, because that makes sense to me -- just like the reuse of function for method decls. Python has demonstrated that people don't have a problem with the same keyword producing functions vs methods on classes (see def).

That said I don't now what you mean by "scoped binding forms" so I can't comment on the latter part of your reply.

let, const, function.

Using these to define properties puns binding declaration as property definition. Maybe that is ok, but some on the committee object in general, not just to the 'var' keyword (which is bad because 'let' is good ;-).

I think we have had trouble assuming things that work for bindings work for properties. For instance, initialization is required for const, but how would your favorite class syntax initialize a const instance property from a constructor parameter? If 'const' goes where you show 'var', the constructor's parameters are not in scope.

We don't want const x; and later x = 42 -- as recently as July's meeting we reaffirmed that const requires an initializer and that is the only write.

# Brendan Eich (14 years ago)

On Sep 29, 2011, at 5:41 PM, Allen Wirfs-Brock wrote:

Presumably the job of the constructor is to establish invariants of the constructed instances. Some of those invariants may involve complex relationships among property values and between the new object and other objects. There are many ways that an object can escape from a constructor before these invariants are fully established. Why is the "const" invariant any more important than other invariants that we aren't attempting to guarantee?

Perhaps it shouldn't be, but then (we've agreed) such invariants as temporal dead zone error on read before initialization are important for const bindings.

So if these const-ish property definitions in classes have different invariants, they ought not use the 'const' keyword. But as we discussed, this takes a toll too. Now users have to learn the new keyword or punctuator, and how it differs from 'const'.

# Oliver Hunt (14 years ago)

On Sep 29, 2011, at 9:41 AM, Allen Wirfs-Brock wrote:

On Sep 28, 2011, at 7:40 PM, Oliver Hunt wrote:

class Foo { var bar; // default value -> undefined var wibble = 5; // initialised fields desugar to statements at the head of the constructor const wiffle; // read barrier on uninitialised

But note that such read barriers must be part of every property access including simple method invocations because the instance can escape from the constructor before it is initialized.

function someMethod(..) { this.bar = foo; // could have a short hand for this.property }

constructor (bar, wibble) { // extra typing due to the removal of horrible punning // but "public bar = bar" or whatever that syntax was is longer // than this.bar theWiderWorld(this); //this escapes from constructor before wibble is initialized

  this.bar = bar;
  this.wibble = wibble;

} }

function elsewhere(obj,key) { var z = obj[key]; //needs read barrier, object might be an incomplete Foo instance and key might be 'wibble' ... }

To accommodate this, each property would seem to need an additional [[Uninitialized]] "attribute" (it could be represented as a distinguished value) which would have to be checked as part of every property access.

You could avoid this simply my not having such "const" declarations and leaving it to the class author to use Object.defineProperty to set the [[Writable]] attribute of such properties to false.

Alternatively, you could just not worry that a const property could escape into the wild in a writable state and the value undefined.

Presumably the job of the constructor is to establish invariants of the constructed instances. Some of those invariants may involve complex relationships among property values and between the new object and other objects. There are many ways that an object can escape from a constructor before these invariants are fully established. Why is the "const" invariant any more important than other invariants that we aren't attempting to guarantee?

I'm not too concerned about the read barrier -- in terms of runtime cost i a) don't believe it would be something that people actually use :D, b) even if they did use it would be sufficiently uncommon to make the perf cost of a read barrier inconsequential and c) if somehow the read barrier did become expensive there are a large number of optimisations that could reasonably trivially get rid of the cost in hot code.

In terms of actual initialisation you could say that constant fields can only be initialised in the constructor -- eg. any write outside the constructor throws (as writing to a non-writable property throws anyway this shouldn't be too hard) and the constructor would just be blessed with a magic ability to write to the slot if it had not yet been initialised.

# Brendan Eich (14 years ago)

On Sep 29, 2011, at 6:06 PM, Oliver Hunt wrote:

I'm not too concerned about the read barrier -- in terms of runtime cost i a) don't believe it would be something that people actually use :D, b) even if they did use it would be sufficiently uncommon to make the perf cost of a read barrier inconsequential and c) if somehow the read barrier did become expensive there are a large number of optimisations that could reasonably trivially get rid of the cost in hot code.

I agree with you, we shouldn't prematurely optimize.

In terms of actual initialisation you could say that constant fields can only be initialised in the constructor -- eg. any write outside the constructor throws (as writing to a non-writable property throws anyway this shouldn't be too hard) and the constructor would just be blessed with a magic ability to write to the slot if it had not yet been initialised.

I'm lulling about how ES4-like this is -- we had const as write-once, and non-null types so we needed guaranteed init of non-null-type-annotated consts -- the last design for ES4 that I recall did exactly what you want here.

But this diverges from 'const' the binding form, and we aren't going to loosen that. What do you think about the objection to using the same keyword for two different approaches?

# Oliver Hunt (14 years ago)

On Sep 29, 2011, at 9:59 AM, Brendan Eich wrote:

On Sep 29, 2011, at 4:47 PM, Oliver Hunt wrote:

There are some on the committee who object to 'var' in particular; others object to using scoped binding forms to define properties in (default mutable) objects. We can go around this block again, though.

The exact token isn't overly important, it could be '@' if we wanted to go down the path of perl and ruby and use random symbols in place of words. I would prefer var, because that makes sense to me -- just like the reuse of function for method decls. Python has demonstrated that people don't have a problem with the same keyword producing functions vs methods on classes (see def).

That said I don't now what you mean by "scoped binding forms" so I can't comment on the latter part of your reply.

let, const, function.

Using these to define properties puns binding declaration as property definition. Maybe that is ok, but some on the committee object in general, not just to the 'var' keyword (which is bad because 'let' is good ;-).

I think we have had trouble assuming things that work for bindings work for properties. For instance, initialization is required for const, but how would your favorite class syntax initialize a const instance property from a constructor parameter? If 'const' goes where you show 'var', the constructor's parameters are not in scope.

We don't want const x; and later x = 42 -- as recently as July's meeting we reaffirmed that const requires an initializer and that is the only write.

See my email from 5 minutes ago :D

I'm not too concerned about const as i don't believe it's something people will use -- i think type guards are something that people would be much more interested in and they don't have the same problem.

If you don't want to separate declaration from arbitrarily complex initialiser you're essentially saying the field declaration must be scattered through the constructor -- at which point finding fields becomes more difficult (you can't declare the field until you've done all the work needed to compute the value -> by necessity const field declarations have to be scattered through the constructor), and you lose object shape guarantees. In that case IMO you should simply drop the pretence of declarative fields and switch to a python/ruby-esque class model where a class is defined by the methods present rather than the methods + fields.

# Brendan Eich (14 years ago)

On Sep 29, 2011, at 6:14 PM, Oliver Hunt wrote:

On Sep 29, 2011, at 9:59 AM, Brendan Eich wrote:

On Sep 29, 2011, at 4:47 PM, Oliver Hunt wrote:

There are some on the committee who object to 'var' in particular; others object to using scoped binding forms to define properties in (default mutable) objects. We can go around this block again, though.

The exact token isn't overly important, it could be '@' if we wanted to go down the path of perl and ruby and use random symbols in place of words. I would prefer var, because that makes sense to me -- just like the reuse of function for method decls. Python has demonstrated that people don't have a problem with the same keyword producing functions vs methods on classes (see def).

That said I don't now what you mean by "scoped binding forms" so I can't comment on the latter part of your reply.

let, const, function.

Using these to define properties puns binding declaration as property definition. Maybe that is ok, but some on the committee object in general, not just to the 'var' keyword (which is bad because 'let' is good ;-).

I think we have had trouble assuming things that work for bindings work for properties. For instance, initialization is required for const, but how would your favorite class syntax initialize a const instance property from a constructor parameter? If 'const' goes where you show 'var', the constructor's parameters are not in scope.

We don't want const x; and later x = 42 -- as recently as July's meeting we reaffirmed that const requires an initializer and that is the only write.

See my email from 5 minutes ago :D

I saw -- your original sketch did not show wiffle being assigned, though :-P.

I'm not too concerned about const as i don't believe it's something people will use -- i think type guards are something that people would be much more interested in and they don't have the same problem.

Two points:

  1. Just because you don't believe people will use something doesn't mean we should settle for sketchy semantics (if they are sketchy). It seems like you're saying "won't be important, don't worry".

  2. Do you mean guards don't have the same problem because we can impute a default well-typed value? I hope not, because we can't -- no static types, rather runtime guards which must be evaluated to know what they mean.

If you don't want to separate declaration from arbitrarily complex initialiser you're essentially saying the field declaration must be scattered through the constructor -- at which point finding fields becomes more difficult (you can't declare the field until you've done all the work needed to compute the value -> by necessity const field declarations have to be scattered through the constructor), and you lose object shape guarantees.

Or at least, the shape guarantees are there but harder to see and reason about at a glance. I totally agree -- public (and private) declarations in the constructor body are losing.

In that case IMO you should simply drop the pretence of declarative fields and switch to a python/ruby-esque class model where a class is defined by the methods present rather than the methods + fields.

See dherman's minimal classes proposal, he minimized it slightly further on the whiteboard, kind of after the meeting / with only a few people looking.

I still think minimal classes is smart if we're going to avoid too high an opportunity cost by working on bigger/alternative class designs.

# Allen Wirfs-Brock (14 years ago)

On Sep 29, 2011, at 10:10 AM, Brendan Eich wrote:

On Sep 29, 2011, at 6:06 PM, Oliver Hunt wrote:

I'm not too concerned about the read barrier -- in terms of runtime cost i a) don't believe it would be something that people actually use :D, b) even if they did use it would be sufficiently uncommon to make the perf cost of a read barrier inconsequential and c) if somehow the read barrier did become expensive there are a large number of optimisations that could reasonably trivially get rid of the cost in hot code.

I agree with you, we shouldn't prematurely optimize.

But we should be thinking about the potential implementation impact of everything we specify.

Just pointing out that not using const properties doesn't mean that the need for a const read barrier on every property access. Without doing closed world whole program analysis every property [[Get]] has to account for the possibility that it may be accessing an uninitialized const property. It may be that an implementation can find a way to fold this check into an initial fast-path guard. By introducing such a semantics we are adding to the complexity of every [[Get]].

This is different form the read barrier for lexical const declaration which can be lexical name specific within a lexically bounded scope.

# Brendan Eich (14 years ago)

On Sep 29, 2011, at 7:46 PM, Allen Wirfs-Brock wrote:

Just pointing out that not using const properties doesn't mean that the need for a const read barrier on every property access. Without doing closed world whole program analysis every property [[Get]] has to account for the possibility that it may be accessing an uninitialized const property. It may be that an implementation can find a way to fold this check into an initial fast-path guard.

That analysis/guard must exist due to getters already.

By introducing such a semantics we are adding to the complexity of every [[Get]].

This is different form the read barrier for lexical const declaration which can be lexical name specific within a lexically bounded scope.

Right.

# Erik Arvidsson (14 years ago)

On Thu, Sep 29, 2011 at 10:22, Brendan Eich <brendan at mozilla.com> wrote:

See dherman's minimal classes proposal, he minimized it slightly further on the whiteboard, kind of after the meeting / with only a few people looking.

I still think minimal classes is smart if we're going to avoid too high an opportunity cost by working on bigger/alternative class designs.

I was going to send something out regarding the "even simpler classes" proposal but I have a meeting with Alex and Bob Nystrom next week to try to make progress on this and I wanted their feedback... however, since the discussion is already taking place here it is.

Even Simpler Classes

There has been a lot of things discussed regarding classes. One thing that I believe was agreed upon was to make classes syntactic sugar for how people use prototype based inheritance today.

However, it seems like all the issues we have seen are due to us trying to solve issues that already exist today with prototype based "classes". These involve (but are not limited to):

  1. Don't let uninitialized objects escape
  2. Ensure the shape of the instance
  3. Initialization of instance properties
  4. Allow const classes
  5. Allow const properties
  6. Play well with future type guards

The question is do we have to solve these? I argue that we don't have to solve these for ES.next. By postponing these we can provide an even simpler class proposal that provides sugar to how people do inheritance today using ES5.

class Derived extends Base { // Object literal body constructor(x) { // constructor this._x = x; // no special form // Disallow return expr? } // optional comma from @awbjs get x() { return this._x; } prop: 42, method() {} // method form @awbjs };

This is syntactic sugar for

var Derived = Base <| function(x) { this._x = x; }.prototype.{ get x() { ... }, prop: 42, method() {} constructor: Derived // hand wave here, needs {enumerable: false} and reference to something not yet available. }

This is pretty close to how both Ruby and Python classes behave and I think we don't have to solve the problems raised above for ES.next.

# Brendan Eich (14 years ago)

On Sep 30, 2011, at 12:22 AM, Erik Arvidsson wrote:

  1. Allow const classes

Hold this thought...

The question is do we have to solve these? I argue that we don't have to solve these for ES.next. By postponing these we can provide an even simpler class proposal that provides sugar to how people do inheritance today using ES5.

Obviously I like your thinking! Don't take my nit-picking below wrong. Please do pick or prove harmless-if-not-beneficial every nit.

class Derived extends Base { // Object literal body constructor(x) { // constructor this._x = x; // no special form // Disallow return expr? } // optional comma from @awbjs get x() { return this._x; } prop: 42, method() {} // method form @awbjs };

Waldemar had some objections to comma elision in object literals, they would apply here too or need to be overcome. If we support only methods, then class body syntax can be its own thing, and drop otiose commas or other separators. But your prop:42 needs a , after it, Waldemar's counterexample used [privateName] as the next key and that would instead "index" into the previous property's value.

So why do we need prototype data properties? I'd drop them as my (4), and keep const classes (since Mark will insist, and the desugaring is easy enough -- more below).

This is syntactic sugar for

var Derived = Base <| function(x) { this._x = x; }.prototype.{ get x() { ... }, prop: 42, method() {} constructor: Derived // hand wave here, needs {enumerable: false} and reference to something not yet available.

No need for "constructor: Derived // hand wave..." because <| clones (or unobservably mutates) its RHS function, and functions get .prototype properties with magic .constructor back-links for free. The mustache you use in the desugaring extends Derived.prototype, it does not lose the default .constructor back-link in that object.

However, you do need a .constructor at the end, so the constructor and not its prototype is assigned to var Derived.

Nit: s/var/let/ -- class should bind as let does, block-scoped and hoisted with temporal dead zone.

Ok, here's the const class desugaring:

const Derived = Object.freeze( Object.freeze( Base <| function(x) { this._x = x; Object.seal(this); }.prototype.{ get x() { ... }, prop: 42, method() {} } ).constructor );

If you buy this, then I think class methods and other properties of the constructor are equally easy. We can defer them, but they do not add novel challenges as const instance variables and barriers to prevent partially initialized objects from leaking do. The only vexing issue is what syntax to use? A "static" keyword is at this point traditional, but we all hate it. Are we being too pure?

# Bob Nystrom (14 years ago)

On Thu, Sep 29, 2011 at 4:22 PM, Erik Arvidsson <erik.arvidsson at gmail.com>wrote:

However, it seems like all the issues we have seen are due to us trying to solve issues that already exist today with prototype based "classes". These involve (but are not limited to):

  1. Don't let uninitialized objects escape
  2. Ensure the shape of the instance
  3. Initialization of instance properties
  4. Allow const classes
  5. Allow const properties
  6. Play well with future type guards

I was tinkering with some syntax ideas last night and had the same revelation. It feels like we've over-constrained ourselves. In particular, if you're willing to discard 2 and 6 (basically not worry about a declarative form for instance properties) I think it gets a lot easier.

If we throw in the sections stuff that we discussed a while back, that could give you something like:

class Monster { constructor(this.name, this.health) {}

attack(target) { log(Monster.attackMessage); }

get isAlive() { return this.health > 0; }

set health(value) { if (value < 0) throw new Error('Health must be non-negative.') this.health = value }

let numAttacks = 0;

class: const attackMessage = 'The monster hits you!'; }

This means:

  • A class body contains only definitions. By default those definitions become properties on the prototype. You can change that by adding a section. Everything after class: goes on the constructor.

  • Instance properties are not declared explicitly. But allowing "this." in the constructor argument list gives you a nice notation for automatically initializing them from arguments.

  • Here I'm using "let" to declare a mutable data property in the class body. We could use whatever. It's a pretty open field since the body doesn't contain arbitrary statements.

This is pretty close to how both Ruby and Python classes behave and I

think we don't have to solve the problems raised above for ES.next.

I like this. Maybe in pursuit of perfect we've lost sight of good?

# Erik Arvidsson (14 years ago)

On Thu, Sep 29, 2011 at 16:54, Brendan Eich <brendan at mozilla.com> wrote:

On Sep 30, 2011, at 12:22 AM, Erik Arvidsson wrote:

  1. Allow const classes

Hold this thought...

The question is do we have to solve these? I argue that we don't have to solve these for ES.next. By postponing these we can provide an even simpler class proposal that provides sugar to how people do inheritance today using ES5.

Obviously I like your thinking! Don't take my nit-picking below wrong. Please do pick or prove harmless-if-not-beneficial every nit.

class Derived extends Base { // Object literal body  constructor(x) {  // constructor    this._x = x;  // no special form    // Disallow return expr?  }  // optional comma from @awbjs  get x() { return this._x; }  prop: 42,  method() {}  // method form @awbjs };

Waldemar had some objections to comma elision in object literals, they would apply here too or need to be overcome. If we support only methods, then class body syntax can be its own thing, and drop otiose commas or other separators. But your prop:42 needs a , after it, Waldemar's counterexample used [privateName] as the next key and that would instead "index" into the previous property's value. So why do we need prototype data properties? I'd drop them as my (4), and keep const classes (since Mark will insist, and the desugaring is easy enough -- more below).

This is syntactic sugar for

var Derived = Base <| function(x) {  this._x = x; }.prototype.{  get x() { ... },  prop: 42,  method() {}  constructor: Derived // hand wave here, needs {enumerable: false} and reference to something not yet available.

No need for "constructor: Derived // hand wave..." because <| clones (or unobservably mutates) its RHS function, and functions get .prototype properties with magic .constructor back-links for free. The mustache you use in the desugaring extends Derived.prototype, it does not lose the default .constructor back-link in that object. However, you do need a .constructor at the end, so the constructor and not its prototype is assigned to var Derived. Nit: s/var/let/ -- class should bind as let does, block-scoped and hoisted with temporal dead zone. Ok, here's the const class desugaring: const Derived =  Object.freeze(   Object.freeze(    Base <| function(x) {     this._x = x;     Object.seal(this);    }.prototype.{     get x() { ... },     prop: 42,     method() {}    }   ).constructor  ); If you buy this, then I think class methods and other properties of the constructor are equally easy. We can defer them, but they do not add novel challenges as const instance variables and barriers to prevent partially initialized objects from leaking do. The only vexing issue is what syntax to use? A "static" keyword is at this point traditional, but we all hate it. Are we being too pure? /be

I would also be fine with a special class body.

I'm also fine with 'static'. Like everybody, I don't really like the name but it is the best we have at the moment.

# Erik Arvidsson (14 years ago)

On Thu, Sep 29, 2011 at 17:08, Bob Nystrom <rnystrom at google.com> wrote:

class Monster {   constructor(this.name, this.health) {}

attack(target) {     log(Monster.attackMessage);   }

get isAlive() {     return this.health > 0;   }

set health(value) {     if (value < 0) throw new Error('Health must be non-negative.')     this.health = value   }

let numAttacks = 0; class:   const attackMessage = 'The monster hits you!'; }

+1

# Brendan Eich (14 years ago)

On Sep 30, 2011, at 1:11 AM, Erik Arvidsson wrote:

On Thu, Sep 29, 2011 at 17:08, Bob Nystrom <rnystrom at google.com> wrote:

class Monster { constructor(this.name, this.health) {}

I <3 this. It beats the (public name, public health) variant in my view by being explicit and not dragging in p-words.

attack(target) { log(Monster.attackMessage); }

get isAlive() { return this.health > 0; }

set health(value) { if (value < 0) throw new Error('Health must be non-negative.') this.health = value }

let numAttacks = 0;

This is great, and I'm still a sections fan, but they mix badly if there are any hints or smells of object literal data property initialiser synax. You (Bob) dodge that by using let, Oliver used var (and IIRC Bob started with var; so did ES4) -- however, Oliver and ES4 by fiat put data properties on the instance, methods on the prototype.

Separately, and a while ago, Alex pointed out that mutable prototype data properties, e..g let attackers = []; in this Monster example, are a footgun. People fail to shadow and mutate a shared singleton.

So why do we need declarative syntax for data properties on the prototype at all? Data properties on prototypes are exceedingly rare and usually a bug.

class: const attackMessage = 'The monster hits you!';

In contrast, constants on constructors are common, and we can define them before any possible use with this desugaring:

const Derived = Base <| function(x) { this._x = x; Object.seal(this); }.prototype.{ get x() { ... }, prop: 42, method() {} }.constructor.{ /* class consts and methods go here */ #attackMessage: "The monster hits you!" };

I mashed up the Monster class const with the Derived example and used the meeting's consensus meaning of # on data property making it non-configurable and non-writable.

# Brendan Eich (14 years ago)

On Sep 30, 2011, at 1:08 AM, Bob Nystrom wrote:

I like this. Maybe in pursuit of perfect we've lost sight of good?

Mainly we've over-constrained the design. The const instance variable problem can be deferred. I claim we don't want any syntactic sugar for data properties on the prototype. Desugaring with <| and .{ is a good exercise, since we want classes as sugar if we can do it (i.e., avoid adding new kernel semantics) and the desugaring can be inspected to look for evaluation problems (use before class-const-definition, e.g.).

Thanks for your messages lately, they have been hugely helpful -- ditto to Oliver and Arv.

# Oliver Hunt (14 years ago)

On Sep 29, 2011, at 6:20 PM, Brendan Eich wrote:

On Sep 30, 2011, at 1:11 AM, Erik Arvidsson wrote:

On Thu, Sep 29, 2011 at 17:08, Bob Nystrom <rnystrom at google.com> wrote:

class Monster { constructor(this.name, this.health) {}

I <3 this. It beats the (public name, public health) variant in my view by being explicit and not dragging in p-words.

I dislike it as it conflates assignment with declaration.

This is great, and I'm still a sections fan, but they mix badly if there are any hints or smells of object literal data property initialiser synax. You (Bob) dodge that by using let, Oliver used var (and IIRC Bob started with var; so did ES4) -- however, Oliver and ES4 by fiat put data properties on the instance, methods on the prototype.

Separately, and a while ago, Alex pointed out that mutable prototype data properties, e..g let attackers = []; in this Monster example, are a footgun. People fail to shadow and mutate a shared singleton.

So why do we need declarative syntax for data properties on the prototype at all? Data properties on prototypes are exceedingly rare and usually a bug.

Agreed. If anything people want constants on "the class"

# Brendan Eich (14 years ago)

On Sep 30, 2011, at 2:25 AM, Oliver Hunt wrote:

On Sep 29, 2011, at 6:20 PM, Brendan Eich wrote:

On Sep 30, 2011, at 1:11 AM, Erik Arvidsson wrote:

On Thu, Sep 29, 2011 at 17:08, Bob Nystrom <rnystrom at google.com> wrote:

class Monster { constructor(this.name, this.health) {}

I <3 this. It beats the (public name, public health) variant in my view by being explicit and not dragging in p-words.

I dislike it as it conflates assignment with declaration.

I don't see assignment at all. If you mean actual parameters bind to formal parameters as if by assignment, sure -- but nothing in the existing function head syntax conflates or abuses the = operator.

I do not believe there is any declaration here. This is simply sugar for

constructor(name, health) { this.name = name; this.health = health; }

# Waldemar Horwat (14 years ago)

On 09/29/2011 05:08 PM, Bob Nystrom wrote:

On Thu, Sep 29, 2011 at 4:22 PM, Erik Arvidsson <erik.arvidsson at gmail.com <mailto:erik.arvidsson at gmail.com>> wrote:

However, it seems like all the issues we have seen are due to us
trying to solve issues that already exist today with prototype based
"classes". These involve (but are not limited to):

1. Don't let uninitialized objects escape
2. Ensure the shape of the instance
3. Initialization of instance properties
4. Allow const classes
5. Allow const properties
6. Play well with future type guards

I was tinkering with some syntax ideas last night and had the same revelation. It feels like we've over-constrained ourselves.

I get that feeling as well.

In particular, if you're willing to discard 2 and 6 (basically not worry about a declarative form for instance properties) I think it gets a lot easier.

Yes, it's easier, but you'd also lose any convenient way of doing 5. 2, 4, and 5 are the most important new features, and there isn't enough value in classes to add them without those. If you find it difficult to come up with a proposal that can accommodate them, it's a sign that you're about to design yourself into a corner.

Constraint 1 is the one above that I'd relax. All simple ways I've seen to to do it are variants of zero-inheritance.

 Waldemar
# Allen Wirfs-Brock (14 years ago)

On Sep 29, 2011, at 4:54 PM, Brendan Eich wrote:

On Sep 30, 2011, at 12:22 AM, Erik Arvidsson wrote: ...

class Derived extends Base { // Object literal body constructor(x) { // constructor this._x = x; // no special form // Disallow return expr?

or if somebody wants to be ore "declarative" for their per instance state they could say: this.{_x: x}; //or this.{x}; if they want to name the inst var x

} // optional comma from @awbjs get x() { return this._x; } prop: 42, method() {} // method form @awbjs };

Waldemar had some objections to comma elision in object literals, they would apply here too or need to be overcome.

I don't see why: let obj = { x: 1, //comma required foo () {} //comma optional y=2} // comma optional (and the same line } is intentional

should be particularly any more objectionable than { x = 1; //semicolon required (if you don't practice ASI function foo () {} //semicolon (really an empty statement) optional y = 2} //semicolon option (most people don't think of this case as being ASI even though it is)

If we support only methods, then class body syntax can be its own thing, and drop otiose commas or other separators. But your prop:42 needs a , after it, Waldemar's counterexample used [privateName] as the next key and that would instead "index" into the previous property's value.

So why do we need prototype data properties? I'd drop them as my (4), and keep const classes (since Mark will insist, and the desugaring is easy enough -- more below).

This is syntactic sugar for

var Derived = Base <| function(x) { this._x = x; }.prototype.{ get x() { ... }, prop: 42, method() {} constructor: Derived // hand wave here, needs {enumerable: false} and reference to something not yet available.

No need for "constructor: Derived // hand wave..." because <| clones (or unobservably mutates) its RHS function, and functions get .prototype properties with magic .constructor back-links for free. The mustache you use in the desugaring extends Derived.prototype, it does not lose the default .constructor back-link in that object.

However, you do need a .constructor at the end, so the constructor and not its prototype is assigned to var Derived.

Nit: s/var/let/ -- class should bind as let does, block-scoped and hoisted with temporal dead zone.

Ok, here's the const class desugaring:

const Derived = Object.freeze( Object.freeze( Base <| function(x) { this._x = x; Object.seal(this); }.prototype.{ get x() { ... }, prop: 42, method() {} } ).constructor );

If you buy this, then I think class methods and other properties of the constructor are equally easy. We can defer them, but they do not add novel challenges as const instance variables and barriers to prevent partially initialized objects from leaking do. The only vexing issue is what syntax to use? A "static" keyword is at this point traditional, but we all hate it. Are we being too pure?

How about: class Derived extends Base { constructor () x { } ... method () { } }. { /* class methods */ }

which desugars in the obvious way.

Class methods are few in number and this emphasis that you are really defining properties on the class objet

# Allen Wirfs-Brock (14 years ago)

On Sep 29, 2011, at 5:10 PM, Erik Arvidsson wrote:

On Thu, Sep 29, 2011 at 16:54, Brendan Eich <brendan at mozilla.com> wrote:

On Sep 30, 2011, at 12:22 AM, Erik Arvidsson wrote: ... A "static" keyword is at this point traditional, but we all hate it. Are we being too pure? /be

I would also be fine with a special class body.

I'm also fine with 'static'. Like everybody, I don't really like the name but it is the best we have at the moment.

Personally, I would choose no support at all for class-side properties if the only other option was calling them "static". I find the use of that keyword for this meaning at least as objectionable as other have found, for example, optional commas after object literal methods.

# Erik Arvidsson (14 years ago)

On Thu, Sep 29, 2011 at 18:38, Waldemar Horwat <waldemar at google.com> wrote:

Yes, it's easier, but you'd also lose any convenient way of doing 5.  2, 4, and 5 are the most important new features, and there isn't enough value in classes to add them without those.  If you find it difficult to come up with a proposal that can accommodate them, it's a sign that you're about to design yourself into a corner.

1. Don't let uninitialized objects escape    2. Ensure the shape of the instance    3. Initialization of instance properties

We have these today and it is not a big problem. Python and Ruby also gets by here.

4. Allow const classes

Object.freeze/seal

5. Allow const properties

Object.defineProperty works for me.

# Brendan Eich (14 years ago)

On Sep 30, 2011, at 2:38 AM, Waldemar Horwat wrote:

On 09/29/2011 05:08 PM, Bob Nystrom wrote:

On Thu, Sep 29, 2011 at 4:22 PM, Erik Arvidsson <erik.arvidsson at gmail.com <mailto:erik.arvidsson at gmail.com>> wrote:

However, it seems like all the issues we have seen are due to us trying to solve issues that already exist today with prototype based "classes". These involve (but are not limited to):

  1. Don't let uninitialized objects escape
  2. Ensure the shape of the instance
  3. Initialization of instance properties
  4. Allow const classes
  5. Allow const properties
  6. Play well with future type guards

I was tinkering with some syntax ideas last night and had the same revelation. It feels like we've over-constrained ourselves.

I get that feeling as well.

In particular, if you're willing to discard 2 and 6 (basically not worry about a declarative form for instance properties) I think it gets a lot easier.

Yes, it's easier, but you'd also lose any convenient way of doing 5.

Is 5 by itself important? Almost all JS uses today (those that don't exploit ES5 Object.defineProperty) use writable instance variables, whether by the closure pattern or the prototypal pattern.

Classes as sugar, if it can be done, means avoiding new kernel semantics until we know how to add them, or there are new long-hands to sugar.

2, 4, and 5 are the most important new features, and there isn't enough value in classes to add them without those.

This seems like a fundamental conflict with "classes as sugar" unless we take the Object.defineProperty semantics as the "salty" long-hand to sugar.

If you find it difficult to come up with a proposal that can accommodate them, it's a sign that you're about to design yourself into a corner.

Or you need to defer 2 and 5 without future hostility. Why is leaving 'const' or anything equivalent for instance variables out, and letting the constructor assign or define all |this| properties, designing anything into a corner?

I agree we should support 4, Mark does too. But it is purely a desugaring step as I've shown.

Constraint 1 is the one above that I'd relax. All simple ways I've seen to to do it are variants of zero-inheritance.

No one wants zero-inheritance, and it doesn't help solve 2 and 5 anyway. Even a zero-inheritance class in the current proposal on the wiki can have "public const k = x*y" in its constructor and have uses of k before the initializer has been evaluated, or exits from the constructor leaving it unevaluated.

# Brendan Eich (14 years ago)

On Sep 30, 2011, at 2:38 AM, Allen Wirfs-Brock wrote:

On Sep 29, 2011, at 4:54 PM, Brendan Eich wrote:

} // optional comma from @awbjs get x() { return this._x; } prop: 42, method() {} // method form @awbjs };

Waldemar had some objections to comma elision in object literals, they would apply here too or need to be overcome.

I don't see why: let obj = { x: 1, //comma required foo () {} //comma optional y=2} // comma optional (and the same line } is intentional

(not sure what y=2 is doing there -- did you mean : not = ?)

The problem comes from computed-property-name syntax:

let obj = {foo() {} [bar]: baz};

You may think that's ok, but here it is all on one line:

let obj = {foo() {} [bar]: baz};

If for some reason you need to go back to old-style foo:function(){}, you lose.

How about: class Derived extends Base { constructor () x {

Looks like a typo, fails to show that the constructor takes an x parameter.

Not sure you intended this, though -- the mustache with class methods may be the main thing:

} ... method () { } }. { /* class methods */ }

which desugars in the obvious way.

Class methods are few in number and this emphasis that you are really defining properties on the class object

That's not true in JS -- we have class methods and constants in the built-ins. In CoffeeScript we have class methods and (Swiss-style) class-side inheritance.

We can certainly defer class-side stuff but there are long-hands to sugar, right now, and the desugaring seems straightforward to me. To avoid the nasty static keyword, we could use Bob's class: labeled section approach.

Labeled sections are hostile to data property initialiser syntax but I claim we don't want that anyway. What say you?

# Bob Nystrom (14 years ago)

On Thu, Sep 29, 2011 at 6:20 PM, Brendan Eich <brendan at mozilla.com> wrote:

On Sep 30, 2011, at 1:11 AM, Erik Arvidsson wrote:

On Thu, Sep 29, 2011 at 17:08, Bob Nystrom <rnystrom at google.com> wrote:

class Monster {

constructor(this.name, this.health) {}

I <3 this. It beats the (public name, public health) variant in my view by being explicit and not dragging in p-words.

Yeah, me too. The big win for me here is that I find this confusing:

class Point { constructor(public x, public y) { x = 2; // is this valid? does it assign to this.x or just the local argument variable? } }

Using this.x in the constructor parameter list maintains a consistent "always use this. to assign to object properties", which I like.

This is great, and I'm still a sections fan, but they mix badly if there are any hints or smells of object literal data property initialiser synax.

Right. Personally, I'm not a fan of mixing object literal style into classes (though I see the appeal from a unification perspective). Maybe that's just my long C++/C#/Java habits.

You (Bob) dodge that by using let, Oliver used var (and IIRC Bob started with var; so did ES4)

Yup. I actually prefer "var" slightly though I understand it gives Mark hives (and for good reason). I don't really have much of a keyword preference here at all as long as it's really short. Could be "thing" for all I care. :)

-- however, Oliver and ES4 by fiat put data properties on the instance,

methods on the prototype.

That feels a little... non-orthogonal? to me. Inside a class when you're defining some property, there's two things you need to specify:

  1. What flavor of property it is: function, constant, field, etc.
  2. What object it gets defined on: constructor, prototype, instance.

Mixing those together so that 1 determines 2 feels kind of arbitrary and not as flexible. I like the idea of the grammar informing one and sections informing 2. Maybe that's just me.

Separately, and a while ago, Alex pointed out that mutable prototype data properties, e..g let attackers = []; in this Monster example, are a footgun. People fail to shadow and mutate a shared singleton.

Yeah, I worry about that too, though I'm leery of giving up generality to dodge that. (People complain as it is that class syntax is too rigid.)

So why do we need declarative syntax for data properties on the prototype at all? Data properties on prototypes are exceedingly rare and usually a bug.

We want a syntax for properties on the constructor, so is it worth it to specifically forbid that notation outside of the class: section?

If we did want to go down the path of a more rigid class pattern (i.e. no data props on prototype, etc.) then I think we could support all of the combinations we care about with a pretty terse notation:

class Point extends SomeBaseClass { // Constructor. constructor(this.x, this.y) { class.lastPoint = this; }

// Constant on class. const ZERO = new Point(0, 0);

// Data property on class. var lastPoint = undefined; // or let

// Function on class. class add(a, b) { return a.add(b); }

// Nested class (data property on class whose value is a class). class Foo { ... }

// Constant on prototype: not supported // Data property on prototype: not supported

// Function on prototype. add(other) { return new Point(this.x + other.x, this.y + other.y); } }

This gets rid of sections, and gets rid of "static" by using "class" instead. It avoids the "class class" problem for nested classes by declaring by fiat that a nested class goes on the constructor.

Thoughts?

# Brendan Eich (14 years ago)

On Sep 30, 2011, at 7:12 PM, Bob Nystrom wrote:

We want a syntax for properties on the constructor, so is it worth it to specifically forbid that notation outside of the class: section?

It's future-proof. Rather do less and get it right than over-reach and regret the stuff that we got wrong.

If we did want to go down the path of a more rigid class pattern (i.e. no data props on prototype, etc.) then I think we could support all of the combinations we care about with a pretty terse notation:

class Point extends SomeBaseClass { // Constructor. constructor(this.x, this.y) { class.lastPoint = this; }

// Constant on class. const ZERO = new Point(0, 0);

// Data property on class. var lastPoint = undefined; // or let

// Function on class. class add(a, b) { return a.add(b); }

// Nested class (data property on class whose value is a class). class Foo { ... }

// Constant on prototype: not supported // Data property on prototype: not supported

// Function on prototype. add(other) { return new Point(this.x + other.x, this.y + other.y); } }

This gets rid of sections, and gets rid of "static" by using "class" instead. It avoids the "class class" problem for nested classes by declaring by fiat that a nested class goes on the constructor.

Thoughts?

I like that nested class is "static" or rather, a property of the outer class's constructor.

This has the irregularity you objected to in the ES4/Oliver "data properties on instances, methods on prototype", but with data properties on the class instead of the instance.

And this is future-hostile to data properties on the prototype, something you're right to consider supporting. Using a class: section is future-friendlier and not much of a hardship.

Try this:

class Point extends SomeBaseClass { // Constructor. constructor(this.x, this.y) { class.lastPoint = this; }

class: // Constant on class. const ZERO = new Point(0, 0);

// Data property on class. var lastPoint = undefined; // or let

// Nested class (data property on class whose value is a class). class Foo { ... }

prototype: // Function on class. class add(a, b) { return a.add(b); }

// Constant on prototype: not supported // Data property on prototype: not supported

// Function on prototype. add(other) { return new Point(this.x + other.x, this.y + other.y); } }

Supporting a prototype: label to allow alternation for minimal change under maintenance is plausible.

For now I think we should minimize and future-proof by forbidding data declarations in the prototype section (the default, implicit section without any label).

# Erik Arvidsson (14 years ago)

On Fri, Sep 30, 2011 at 10:12, Bob Nystrom <rnystrom at google.com> wrote:

// Constant on class.   const ZERO = new Point(0, 0);

I don't like this. It is too magic and I don't expect people to remember to use class.ZERO or MyClass.ZERO over this.ZERO.

// Function on class.   class add(a, b) {     return a.add(b);   }   // Nested class (data property on class whose value is a class).   class Foo {     ...   }

These two look too similar for my liking. I know people don't like 'static' but it is a lot clearer what the intent is.

In general I don't like having some things go on the prototype and some on the function/class. I see refactoring hazards (changes from const to var/let will break)

One of the things I like about the object literal syntax is that it doesn't seem like things might be in scope. Take this example (with intentional error):

class Point { var x = 0; var y = 0; distance(other) { return Math.sqrt(x * other.x + y * other.y); } }

it is very tempting to think that var x and var y are in scope. If an object literal is used at least we have the current behavior to steer people into doing the right thing.

# Brendan Eich (14 years ago)

On Sep 30, 2011, at 7:56 PM, Brendan Eich wrote:

class Point extends SomeBaseClass { // Constructor. constructor(this.x, this.y) { class.lastPoint = this; }

class: // Constant on class. const ZERO = new Point(0, 0);

// Data property on class. var lastPoint = undefined; // or let

// Nested class (data property on class whose value is a class). class Foo { ... }

prototype: // Function on class. class add(a, b) { return a.add(b); }

Oops, misread this -- or didn't read it at all! Arv's right, this is too confusing. Anyway, easy to fix in my counterproposal: remove the class prefix-keyword (which is never allowed) and move this "static add method" up into the class: section.

# Bob Nystrom (14 years ago)

On Fri, Sep 30, 2011 at 10:56 AM, Brendan Eich <brendan at mozilla.com> wrote:

On Sep 30, 2011, at 7:12 PM, Bob Nystrom wrote:

We want a syntax for properties on the constructor, so is it worth it to specifically forbid that notation outside of the class: section?

It's future-proof. Rather do less and get it right than over-reach and regret the stuff that we got wrong.

Ah, that makes sense.

If we did want to go down the path of a more rigid class pattern (i.e. no data props on prototype, etc.) then I think we could support all of the combinations we care about with a pretty terse notation:

class Point extends SomeBaseClass { // Constructor. constructor(this.x, this.y) { class.lastPoint = this; }

// Constant on class. const ZERO = new Point(0, 0);

// Data property on class. var lastPoint = undefined; // or let

// Function on class. class add(a, b) { return a.add(b); }

// Nested class (data property on class whose value is a class). class Foo { ... }

// Constant on prototype: not supported // Data property on prototype: not supported

// Function on prototype. add(other) { return new Point(this.x + other.x, this.y + other.y); } }

This gets rid of sections, and gets rid of "static" by using "class" instead. It avoids the "class class" problem for nested classes by declaring by fiat that a nested class goes on the constructor.

Thoughts?

I like that nested class is "static" or rather, a property of the outer class's constructor.

This has the irregularity you objected to in the ES4/Oliver "data properties on instances, methods on prototype", but with data properties on the class instead of the instance.

And this is future-hostile to data properties on the prototype, something you're right to consider supporting. Using a class: section is future-friendlier and not much of a hardship.

Try this:

class Point extends SomeBaseClass { // Constructor. constructor(this.x, this.y) { class.lastPoint = this; }

class: // Constant on class. const ZERO = new Point(0, 0);

// Data property on class. var lastPoint = undefined; // or let

// Nested class (data property on class whose value is a class). class Foo { ... }

prototype: // Function on class. class add(a, b) { return a.add(b); }

// Constant on prototype: not supported // Data property on prototype: not supported

// Function on prototype. add(other) { return new Point(this.x + other.x, this.y + other.y); } }

Oh, nice. I didn't even consider that "class:" sections also solve the "class class" nested problem. For those following along at home, here's a slightly cleaned up version of that:

class Point extends SomeBaseClass { // Constructor. constructor(this.x, this.y) { class.lastPoint = this; }

// Constant on prototype: not supported // Data property on prototype: not supported

// Function on prototype. add(other) { return new Point(this.x + other.x, this.y + other.y); }

class: // Constant on class. const ZERO = new Point(0, 0);

// Data property on class. var lastPoint = undefined; // or let

// Nested class (data property on class whose value is a class). class Foo { ... }

// Function on class. add(a, b) { return a.add(b); } }

This looks pretty nice to me.

# Brendan Eich (14 years ago)

On Sep 30, 2011, at 7:57 PM, Erik Arvidsson wrote:

One of the things I like about the object literal syntax is that it doesn't seem like things might be in scope. Take this example (with intentional error):

class Point { var x = 0; var y = 0; distance(other) { return Math.sqrt(x * other.x + y * other.y); } }

it is very tempting to think that var x and var y are in scope. If an object literal is used at least we have the current behavior to steer people into doing the right thing.

Two thoughts:

  1. Oliver and others do want x and y to be in scope somehow. They can't be via the |this| parameter object on the scope chain, though. That's dynamic scope (the prototype is extensible, or a prototype, possibly Object.prototype, is). But the desire to avoid this. prefixing is natural and predictable for people coming from C++, Java, etc. Should we consider supporting this expectation somehow, rather than steering people away from it with less natural syntax?

  2. The variant Bob and I were hacking on just now allows class data properties to be declared using const or var (shall we say, let). This still can lead to the expectation that class property names are in scope for class methods, or even all methods. Bob's constructor set class.lastPoint = this; but for some folks (Java heads), it would be more natural to set lastPoint = this. I'm not defending, just noting the declaration implies name is in scope problem you cite applies to class: properties in the variant proposal.

If we wanted to support x and y bindings in methods as lexical aliases to this.x and this.y, given declarations, could we do it? Doing this would hide any outer x and y. Same question goes for lastPoint, although it seems to me we are free to shun the Java influence by not aliasing unqualified lastPoint to Point.lastPoint in prototype methods. We could alias class property names in class methods too.

What does it mean to alias properties with lexical bindings? The referent properties' object must outlive the lexical scope. That's easy with |this| in method activations, and I think also can be arranged for the class within a class method.

Another implication is that the referent properties are non-configurable. That leaves writable: true, which creates an alias analysis problem. Any property reference in a method, say foo.x, could alias the storage referenced by lexical x, to wit: this.x. So reads and writes would have be coherent, a challenge to optimizing JIT-based VMs. Not a huge problem for implementors, but could this make for confusion among users because x = 42 updates this.x?

See discussion:block_expressions#let_ref_as_a_reformed_with where Lars Hansen mused about Modula 3's WITH statement, which does alias fresh lexical names to variables including writable variables.

Adding lexical aliases to certain properties does add new semantics. Dave reminded us (me anyway) at last week's TC39 meeting that we do not rule out new semantics if warranted, we simply prefer desugaring to existing kernel semantics where that works, and we try hard to avoid additions.

My question for everyone: will users expect declared property names to be in-scope, no matter what the syntax for declaring or defining them is? I suspect so, and that makes me think we're not going to do well by "counter-steering" with object literal property init syntax.

# Oliver Hunt (14 years ago)

On Sep 30, 2011, at 11:53 AM, Brendan Eich wrote:

On Sep 30, 2011, at 7:57 PM, Erik Arvidsson wrote:

One of the things I like about the object literal syntax is that it doesn't seem like things might be in scope. Take this example (with intentional error):

class Point { var x = 0; var y = 0; distance(other) { return Math.sqrt(x * other.x + y * other.y); } }

it is very tempting to think that var x and var y are in scope. If an object literal is used at least we have the current behavior to steer people into doing the right thing.

Two thoughts:

  1. Oliver and others do want x and y to be in scope somehow. They can't be via the |this| parameter object on the scope chain, though. That's dynamic scope (the prototype is extensible, or a prototype, possibly Object.prototype, is). But the desire to avoid this. prefixing is natural and predictable for people coming from C++, Java, etc. Should we consider supporting this expectation somehow, rather than steering people away from it with less natural syntax?

My recent proposals have not been placing member fields in scope (all my examples use this.blah), they have merely been providing a declarative syntax to define the shape of an instance of the object. Basically i've started leaning towards python-esque classes, with optional declarative field syntax (which also has the benefit of saving you from the setter on the prototype trap that you suffer when not using object literals).

I also see the constructor(this.x, ..) stuff as syntactic sugar that is unrelated to the actual class semantics -- that syntax could be added at a later date and would not impact the behaviour of classes, and if it were added i see no reason to restrict it solely "constructors". That said I don't like the syntax at all :D

# Brendan Eich (14 years ago)

On Sep 30, 2011, at 9:09 PM, Oliver Hunt wrote:

  1. Oliver and others do want x and y to be in scope somehow. They can't be via the |this| parameter object on the scope chain, though. That's dynamic scope (the prototype is extensible, or a prototype, possibly Object.prototype, is). But the desire to avoid this. prefixing is natural and predictable for people coming from C++, Java, etc. Should we consider supporting this expectation somehow, rather than steering people away from it with less natural syntax?

My recent proposals have not been placing member fields in scope (all my examples use this.blah), they have merely been providing a declarative syntax to define the shape of an instance of the object.

Ok, noted -- sorry, I meant to write "Gavin" not "Oliver" ;-). The idea of instance variables being in scope does keep coming up.

I also see the constructor(this.x, ..) stuff as syntactic sugar that is unrelated to the actual class semantics -- that syntax could be added at a later date and would not impact the behaviour of classes,

Yes, agreed as far as your statement goes. We could also add it an earlier date and not impact classes, I hope you'll agree. Just trying not to cast it out into the far future.

Also, I can make a conservative case for restricting "this." parameter name prefixing to constructor. That's where it pays off, and we don't have to worry about unintended consequences in non-constructors.

and if it were added i see no reason to restrict it solely "constructors". That said I don't like the syntax at all :D

What, if any, syntax for avoiding repeating each member name three times, do you prefer?

# Axel Rauschmayer (14 years ago)

As an aside: love how class is used in "Point extends SomeBaseClass", below (including "class.lastPoint").

From: Brendan Eich <brendan at mozilla.com> Subject: Re: Sep 27 meeting notes Date: September 30, 2011 20:53:33 GMT+02:00 To: Erik Arvidsson <erik.arvidsson at gmail.com> Cc: es-discuss <es-discuss at mozilla.org>

My question for everyone: will users expect declared property names to be in-scope, no matter what the syntax for declaring or defining them is? I suspect so, and that makes me think we're not going to do well by "counter-steering" with object literal property init syntax.

I consider myself a Java head, but I never liked member variables coming into the scope of methods. The explicitness and symmetry (when having an argument whose type is the same as that of "this") it brings are great. For me, regularity is more important for my brain’s decoding speed than seeing less characters.

Instance property init syntax seems like a bad idea. I’m often seeing people using prototype properties for this purpose, which works but feels wrong. It gives people the wrong idea. It’s best to tell newbies:

Forget most of what you know about classes and stick to the following rules:

  1. Everything that goes into the instance must be put inside the constructor.
  2. Methods that are shared between instances go into the prototype.
  3. Class methods go into the "class:" section.

.#1 goes against the grain of the Java school of thought, but it is very logical once you accept it. And (especially with "this." in the parameter declaration) it avoids redundancies such as:

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

I’m still slightly worried about correspondences: Given the code below, Point is a function, but "add(other)" is not one of its methods. The way that the default (epsilon section) is currently handled suggests that Point is the prototype object (it even has a method called "constructor"…).

I see two solutions:

  1. Properties in the default (epsilon) section become class properties. Probably not a good idea, because it would thoroughly confuse people coming from other languages.
  2. Require a prototype: section for methods.

I’m not entirely happy with #2, either. Maybe there is some other way to achieve this—it would be nice that if people dug a little deeper (e.g. examining what’s inside Point), what they found conformed to the expectations they have from working with class literals.

# Oliver Hunt (14 years ago)

On Sep 30, 2011, at 12:13 PM, Brendan Eich wrote:

and if it were added i see no reason to restrict it solely "constructors". That said I don't like the syntax at all :D

What, if any, syntax for avoiding repeating each member name three times, do you prefer?

Personally I don't spend my entire life writing constructors so the committees obsession with reducing the token count in a constructor still confuses me.

Most code is not constructors.

# Axel Rauschmayer (14 years ago)

Correction (in square brackets):

# Bob Nystrom (14 years ago)

On Fri, Sep 30, 2011 at 11:53 AM, Brendan Eich <brendan at mozilla.com> wrote:

  1. Oliver and others do want x and y to be in scope somehow. They can't be via the |this| parameter object on the scope chain, though. That's dynamic scope (the prototype is extensible, or a prototype, possibly Object.prototype, is). But the desire to avoid this. prefixing is natural and predictable for people coming from C++, Java, etc. Should we consider supporting this expectation somehow, rather than steering people away from it with less natural syntax?

When I first started using JS heavily, having to type this. everywhere drove me up the wall. Now I love it. It's definitely more verbose (and I really like CoffeeScript's answer here) but I think it's worth it to have a clear distinction between variables in lexical scope and properties of objects.

When I went back to Java from JS, I found not having to do this. felt weird and magical. I think it works OK in a static language, but I'm not sure if it's asking for trouble in a dynamic one.

My question for everyone: will users expect declared property names to be in-scope, no matter what the syntax for declaring or defining them is? I suspect so, and that makes me think we're not going to do well by "counter-steering" with object literal property init syntax.

I'd like to hope not. Aside from the now dead with, I didn't think there were any places in the language where properties were in scope now, so I don't know if they would expect that to change.

# Axel Rauschmayer (14 years ago)

I wholeheartedly agree with not obsessing about the token count. I’m very sensitive to context in this regard: in some contexts, I don’t mind or even welcome additional tokens, in other contexts I hate them.

However, avoiding repeating the same identifier several times seems worth it to me (this kind of redundancy goes beyond dropping a token or using shorter tokens).

Java: class Point { public int x; public int y; public Point(int x, int y) { this.x = x; this.y = y; } }

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

Java is even more fun if you have getters and setters for x and y.

# Bob Nystrom (14 years ago)

On Fri, Sep 30, 2011 at 12:59 PM, Axel Rauschmayer <axel at rauschma.de> wrote:

I wholeheartedly agree with not obsessing about the token count. I’m very sensitive to context in this regard: in some contexts, I don’t mind or even welcome additional tokens, in other contexts I hate them.

However, avoiding repeating the same identifier several times seems worth it to me (this kind of redundancy goes beyond dropping a token or using shorter tokens).

+1 to this. I don't think a shorthand for this is the most important part of a class syntax, but it seems like a relatively easy win for a part of the language that currently feels unnecessarily redundant.

# Erik Arvidsson (14 years ago)

I also like it but I am slightly sympathetic with Oliver's argument. I don't think this is where the issues are at the moment.

I think the main issue at the moment is to get to an agrement whether a minimal class proposal is acceptable for ES.next.

# Claus Reinke (14 years ago)

class Point { var x = 0; var y = 0; distance(other) { return Math.sqrt(x * other.x + y * other.y); } }

it is very tempting to think that var x and var y are in scope.

Would the following analogy with modules help?

Think of 'this' as 
- the export object of the constructor method
- an import object of all methods.

Then one might want nice syntax for moving between local bindings and import/export objects, without risks to lexical scoping. Which is the same problem modules have, isn't it?

Could one use punning ({x} standing for {x=x}), destructuring, and returning 'this'?

distance(other) {
    let {x,y} = this;    // import
    return Math.sqrt(x * other.x + y * other.y);
}

constructor(x,y) {
    return {x,y};    // export
}

To enable further code between 'this' initialization and constructor return, might one permit assignment to 'this'?

constructor(x,y) {
    this = {x,y};    // partial export
    // more setup code
} // defaults to returning 'this'

Not taking sides, just wanted to mention the analogy..

Claus clausreinke.github.com

# Waldemar Horwat (14 years ago)

On 09/30/2011 01:20 AM, Brendan Eich wrote:

On Sep 30, 2011, at 2:38 AM, Waldemar Horwat wrote:

On 09/29/2011 05:08 PM, Bob Nystrom wrote:

On Thu, Sep 29, 2011 at 4:22 PM, Erik Arvidsson<erik.arvidsson at gmail.com<mailto:erik.arvidsson at gmail.com>> wrote:

However, it seems like all the issues we have seen are due to us
trying to solve issues that already exist today with prototype based
"classes". These involve (but are not limited to):

1. Don't let uninitialized objects escape
2. Ensure the shape of the instance
3. Initialization of instance properties
4. Allow const classes
5. Allow const properties
6. Play well with future type guards

I was tinkering with some syntax ideas last night and had the same revelation. It feels like we've over-constrained ourselves.

I get that feeling as well.

In particular, if you're willing to discard 2 and 6 (basically not worry about a declarative form for instance properties) I think it gets a lot easier.

Yes, it's easier, but you'd also lose any convenient way of doing 5.

Is 5 by itself important? Almost all JS uses today (those that don't exploit ES5 Object.defineProperty) use writable instance variables, whether by the closure pattern or the prototypal pattern.

Classes as sugar, if it can be done, means avoiding new kernel semantics until we know how to add them, or there are new long-hands to sugar.

2, 4, and 5 are the most important new features, and there isn't enough value in classes to add them without those.

This seems like a fundamental conflict with "classes as sugar" unless we take the Object.defineProperty semantics as the "salty" long-hand to sugar.

Without 2, 4, and 5, object initializers are close enough to make having an extra class facility not carry its weight.

 Waldemar
# Sam Tobin-Hochstadt (14 years ago)

On Fri, Sep 30, 2011 at 5:11 PM, Claus Reinke <claus.reinke at talk21.com> wrote:

class Point {  var x = 0;  var y = 0;  distance(other) {   return Math.sqrt(x * other.x + y * other.y);  } }

it is very tempting to think that var x and var y are in scope.

Would the following analogy with modules help?

This analogy came up at the recent face-to-face meeting, and I want to disagree with it here too, at least if your analogizing to ES.next modules.

Think of 'this' as    - the export object of the constructor method   - an import object of all methods.

Modules don't have an "export object", or an "import object". Module instance objects are values which reflect the exports of a module, but they aren't the module themselves. And regardless, the semantics of modules is not the same as the import and export objects of CommonJS .

Then one might want nice syntax for moving between local bindings and import/export objects, without risks to lexical scoping. Which is the same problem modules have, isn't it?

Modules don't have a problem with this, because they don't have import/export objects.

Could one use punning ({x} standing for {x=x}), destructuring, and returning 'this'?

distance(other) {       let {x,y} = this;    // import       return Math.sqrt(x * other.x + y * other.y);   }

constructor(x,y) {       return {x,y};    // export   }

First, these aren't like export or import. Second, we've agreed, I think, that returning something other than 'this' from class constructors is a non-goal for ES.next classes, to be revisited for future harmonizing.

# Oliver Hunt (14 years ago)

On Sep 30, 2011, at 2:17 PM, Waldemar Horwat wrote:

On 09/30/2011 01:20 AM, Brendan Eich wrote:

On Sep 30, 2011, at 2:38 AM, Waldemar Horwat wrote:

On 09/29/2011 05:08 PM, Bob Nystrom wrote:

On Thu, Sep 29, 2011 at 4:22 PM, Erik Arvidsson<erik.arvidsson at gmail.com<mailto:erik.arvidsson at gmail.com>> wrote:

However, it seems like all the issues we have seen are due to us trying to solve issues that already exist today with prototype based "classes". These involve (but are not limited to):

  1. Don't let uninitialized objects escape
  2. Ensure the shape of the instance
  3. Initialization of instance properties
  4. Allow const classes
  5. Allow const properties
  6. Play well with future type guards

I was tinkering with some syntax ideas last night and had the same revelation. It feels like we've over-constrained ourselves.

I get that feeling as well.

In particular, if you're willing to discard 2 and 6 (basically not worry about a declarative form for instance properties) I think it gets a lot easier.

Yes, it's easier, but you'd also lose any convenient way of doing 5.

Is 5 by itself important? Almost all JS uses today (those that don't exploit ES5 Object.defineProperty) use writable instance variables, whether by the closure pattern or the prototypal pattern.

Classes as sugar, if it can be done, means avoiding new kernel semantics until we know how to add them, or there are new long-hands to sugar.

2, 4, and 5 are the most important new features, and there isn't enough value in classes to add them without those.

This seems like a fundamental conflict with "classes as sugar" unless we take the Object.defineProperty semantics as the "salty" long-hand to sugar.

Without 2, 4, and 5, object initializers are close enough to make having an extra class facility not carry its weight.

I disagree, people like python's classes which don't have any of these guarantees. What people want from classes seems to be a syntax that provides "classical" inheritance. Yes the current features of ES5 allow you to make such a construct, but being possible vs. being pleasant are not the same thing.

The only thing i really feel that i (personally) would want is #4. Part of me wants #2 but python has shown that's not strictly necessary.

I think we can add python style (in terms of behaviour) classes with a fairly lightweight syntax, and that syntax would not constrain future additions (declarative fields, random syntax in the argument list to aid initialisation ;) )

I think in terms of #2 there are a few different approaches to consider:

  • No declarative fields -> all properties are defined with this.blah =, etc
  • Declarative fields but no shape guarantees -> additional properties can be added dynamically with this.blah, etc (essentially an instance of a class is extensible), declarative fields are all !configurable on the object
  • Declarative fields with shape guarantees -> logically the object is sealed, with the caveat that an instance of a subclass has all the fields of both it, and its superclass.

Resolving these and the many other issues of declarative properties would take a substantial amount of time, but does not block a basic class definition syntax that mirrors what people currently have to use libraries for.

# Axel Rauschmayer (14 years ago)

From: Waldemar Horwat <waldemar at google.com> Subject: Re: Sep 27 meeting notes Date: September 30, 2011 23:17:04 GMT+02:00 To: Brendan Eich <brendan at mozilla.com> Cc: es-discuss <es-discuss at mozilla.org>, Erik Arvidsson <erik.arvidsson at gmail.com>

Without 2, 4, and 5, object initializers are close enough to make having an extra class facility not carry its weight.

Can you show code that backs up that assertion? (I’m curious, not dismissive.)

Wasn’t it David Herman a while ago who listed a minimal feature list? For me it would be:

  1. Super property references (mainly methods)
  2. Super constructor references
  3. Subclassing (mainly wiring the prototypes)
  4. Defining a class as compactly as possible (with subclassing, it is painful that one has to assemble so many pieces).
  5. Having a standard construct that fosters IDE support. Currently there are too many inheritance APIs out there, making IDE support nearly impossible.
  6. A platform on which to build future extensions (traits!).

Allen’s object literal extensions give us #1 and #2. His prototype operator gives us #3. #4 can be done via Allen’s pattern or by introducing the methods

  • Function.prototype.withPrototypeProperties(props)
  • Function.prototype.withClassProperties(props)

I’m not sure about #5 (I’d consider class literals a plus here). #6 can be postponed if we can get 1-5 by other means, but there will be a price to pay if two competing ways of defining classes have to be used in ES.next.next.

# Allen Wirfs-Brock (14 years ago)

below

On Sep 30, 2011, at 2:49 PM, Axel Rauschmayer wrote:

From: Waldemar Horwat <waldemar at google.com> ...

  1. Super property references (mainly methods)
  2. Super constructor references
  3. Subclassing (mainly wiring the prototypes)
  4. Defining a class as compactly as possible (with subclassing, it is painful that one has to assemble so many pieces).
  5. Having a standard construct that fosters IDE support. Currently there are too many inheritance APIs out there, making IDE support nearly impossible.
  6. A platform on which to build future extensions (traits!).

Allen’s object literal extensions give us #1 and #2. His prototype operator gives us #3. #4 can be done via Allen’s pattern or by introducing the methods

  • Function.prototype.withPrototypeProperties(props)
  • Function.prototype.withClassProperties(props)

I’m not sure about #5 (I’d consider class literals a plus here).

I'd argue that my pattern also supports #5 as IDE's can recognize the pattern. An imperative class definition of all that most Smalltalk implementations every had and they generally had fine IDEs.

# David Herman (14 years ago)

I pretty much agree with Axel's goals listed here. But I don't think Mark or Waldemar do. As Erik says, this seems to be the biggest sticking point.

As for IDE's, I'm with Allen: we don't need to bend over backwards. The worst offender I've seen was the design that involved uninitialized property declarations in the class body, only to assign the properties in the constructor body anyway. This just seems like extra make-work for no particular gain; I'd rather let the IDE do the work of inferring the properties from the body. (If you want to document type information about the properties in comments or something, put them with the class declaration or the constructor declaration.)

But when Waldemar said:

This seems like a fundamental conflict with "classes as sugar" unless we take the Object.defineProperty semantics as the "salty" long-hand to sugar.

Without 2, 4, and 5, object initializers are close enough to make having an extra class facility not carry its weight.

I disagree. The super patterns are really painful and easy to get wrong in existing JS, and the benefits of combining a prototype declaration and constructor declaration in a single form shouldn't be dismissed. It's noticeably more readable and it codifies and standardizes a common pattern.

# Bob Nystrom (14 years ago)

On Fri, Sep 30, 2011 at 3:17 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

I'd argue that my pattern also supports #5 as IDE's can recognize the pattern. An imperative class definition of all that most Smalltalk implementations every had and they generally had fine IDEs.

I'm not a Smalltalker, but I thought Smalltalk IDEs were image-based and worked on the objects directly live in memory. At that point, it didn't matter how you generated a class: from text, imperatively, by PEEKing and POKEing bits in memory.

That isn't true of most IDEs today that are just working on the program text itself. With today's editors, the textual format matters. (We might rightly lament that fact, but it does seem to be the field we've got to play on right now.)

# Bob Nystrom (14 years ago)

On Fri, Sep 30, 2011 at 3:38 PM, David Herman <dherman at mozilla.com> wrote:

I disagree. The super patterns are really painful and easy to get wrong in existing JS, and the benefits of combining a prototype declaration and constructor declaration in a single form shouldn't be dismissed. It's noticeably more readable and it codifies and standardizes a common pattern.

Agreed completely.

With the class proposal we're mostly focused on the contentious parts, but I think it's easy to forget how much nicer just having extends Foo and super.foo() would be over what we have today.

I also personally really dig seeing an entire kind-of-thing defined in one nice neat curly block instead of a constructor function and a bunch of dangling imperative modifications after that.

# Allen Wirfs-Brock (14 years ago)

On Sep 30, 2011, at 3:40 PM, Bob Nystrom wrote:

On Fri, Sep 30, 2011 at 3:17 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote: I'd argue that my pattern also supports #5 as IDE's can recognize the pattern. An imperative class definition of all that most Smalltalk implementations every had and they generally had fine IDEs.

I'm not a Smalltalker, but I thought Smalltalk IDEs were image-based and worked on the objects directly live in memory. At that point, it didn't matter how you generated a class: from text, imperatively, by PEEKing and POKEing bits in memory.

Not enterprise class Smalltalk IDE's such as Digitalk's Team/V or IBM/OTI Envy Developer. They did comprehensive analysis of Smalltalk source code before loading it into the image. This enabled conflict detections, version diff'ing, browsing/editing of unloaded class definitions, etc.

I can speak definitively about Team/V as I was its architect. In processed source code into an internal object model (essentially a attributed AST) and this is what the tools operated upon, not the actual runtime representation of the program (well, there were versions of those models that could reflect upon the runtime structure). Building the models from source code included recognizing imperative patterns used to define classes.

That isn't true of most IDEs today that are just working on the program text itself. With today's editors, the textual format matters. (We might rightly lament that fact, but it does seem to be the field we've got to play on right now.)

Some might argue whether this is a step forward or not (personally, I think a stable source code representation is important). However, it doesn't say anything about the feasibility of tool recognizing usage patterns. In fact, isn't that exactly what refactoring tools and code quality analyzers do?

# Brendan Eich (14 years ago)

On Sep 30, 2011, at 9:39 PM, Oliver Hunt wrote:

On Sep 30, 2011, at 12:13 PM, Brendan Eich wrote:

and if it were added i see no reason to restrict it solely "constructors". That said I don't like the syntax at all :D

What, if any, syntax for avoiding repeating each member name three times, do you prefer?

Personally I don't spend my entire life writing constructors so the committees obsession with reducing the token count in a constructor still confuses me.

No one is obsessed. We're trying not to suck by comparison to languages ranging from CoffeeScript to Scala, 'sall.

Most code is not constructors.

Yeah, so? We can generalize for all methods as you suggested, but I somehow doubt that was your point.

# Brendan Eich (14 years ago)

On Sep 30, 2011, at 9:51 PM, Bob Nystrom wrote:

On Fri, Sep 30, 2011 at 11:53 AM, Brendan Eich <brendan at mozilla.com> wrote:

  1. Oliver and others do want x and y to be in scope somehow. They can't be via the |this| parameter object on the scope chain, though. That's dynamic scope (the prototype is extensible, or a prototype, possibly Object.prototype, is). But the desire to avoid this. prefixing is natural and predictable for people coming from C++, Java, etc. Should we consider supporting this expectation somehow, rather than steering people away from it with less natural syntax?

When I first started using JS heavily, having to type this. everywhere drove me up the wall. Now I love it. It's definitely more verbose (and I really like CoffeeScript's answer here) but I think it's worth it to have a clear distinction between variables in lexical scope and properties of objects.

When I went back to Java from JS, I found not having to do this. felt weird and magical. I think it works OK in a static language, but I'm not sure if it's asking for trouble in a dynamic one.

As I wrote, we wouldn't put |this| on the scope chain. Rather, declared (by whatever special form) instance variable names would create lexical bindings that alias |this| properties of the same name.

My question for everyone: will users expect declared property names to be in-scope, no matter what the syntax for declaring or defining them is? I suspect so, and that makes me think we're not going to do well by "counter-steering" with object literal property init syntax.

I'd like to hope not. Aside from the now dead with, I didn't think there were any places in the language where properties were in scope now, so I don't know if they would expect that to change.

No |with|, see above.

# Brendan Eich (14 years ago)

On Sep 30, 2011, at 11:17 PM, Waldemar Horwat wrote:

2, 4, and 5 are the most important new features, and there isn't enough value in classes to add them without those.

This seems like a fundamental conflict with "classes as sugar" unless we take the Object.defineProperty semantics as the "salty" long-hand to sugar.

Without 2, 4, and 5, object initializers are close enough to make having an extra class facility not carry its weight.

I believe others don't feel that way. I'm interested in 4 at most, since we haven't come up with a way to do 2 and 5 that works, and object literals do not count as "sugar" for the constructor/prototype pattern. You need a bunch of manual nesting and assigning, even if we add |super| as Allen has spec'ed.

# Axel Rauschmayer (14 years ago)

That isn't true of most IDEs today that are just working on the program text itself. With today's editors, the textual format matters. (We might rightly lament that fact, but it does seem to be the field we've got to play on right now.)

Some might argue whether this is a step forward or not (personally, I think a stable source code representation is important). However, it doesn't say anything about the feasibility of tool recognizing usage patterns. In fact, isn't that exactly what refactoring tools and code quality analyzers do?

True. The Eclipse JDT keep all kinds of models and indices.

# Waldemar Horwat (14 years ago)

On 09/30/2011 04:37 PM, Brendan Eich wrote:

since we haven't come up with a way to do 2 and 5 that works,

That's what makes this into a dead end. Worse, by claiming the class syntax you'd be precluding finding a different way that works in the future.

 Waldemar
# Bob Nystrom (14 years ago)

On Fri, Sep 30, 2011 at 4:57 PM, Waldemar Horwat <waldemar at google.com>wrote:

On 09/30/2011 04:37 PM, Brendan Eich wrote:

since we haven't come up with a way to do 2 and 5 that works,

That's what makes this into a dead end. Worse, by claiming the class syntax you'd be precluding finding a different way that works in the future.

Can you go into a bit more detail about why those two points matter so much and what they mean for you? Even if it is a dead end, it would be nice to know how far we have to backtrack before we can try to start down a different path.

  1. Ensure the shape of the instance

This seems like a performance optimization to me, unless there's other details I'm not aware of. I don't know how important it is for the language spec to cater to that, but it seems like we've gotten along without it pretty well up until now.

  1. Allow const properties

The syntax proposed so far does enable this for properties on the prototype and class, just not for the instance. Is that enough, or do we need to go farther?

# Axel Rauschmayer (14 years ago)

[cc-ing es-discuss]

On Oct 1, 2011, at 1:59 , Juan Ignacio Dopazo wrote:

On Fri, Sep 30, 2011 at 6:49 PM, Axel Rauschmayer <axel at rauschma.de> wrote: Wasn’t it David Herman a while ago who listed a minimal feature list? For me it would be:

  1. Super property references (mainly methods)
  2. Super constructor references
  3. Subclassing (mainly wiring the prototypes)
  4. Defining a class as compactly as possible (with subclassing, it is painful that one has to assemble so many pieces).
  5. Having a standard construct that fosters IDE support. Currently there are too many inheritance APIs out there, making IDE support nearly impossible.
  6. A platform on which to build future extensions (traits!).

I'll add a couple of things:

  1. Don't forget super getters/setters. Those are helpful.

Not sure how often they would be used and whether they need to be part of a minimal feature set.

  1. This should mean that there must be a way of setting class/static properties

Exactly, that’s one of the pieces.

  1. Encapsulation/privacy should be there somewhere.

The private names proposal should work, as a first solution.

# Brendan Eich (14 years ago)

On Oct 1, 2011, at 1:57 AM, Waldemar Horwat wrote:

On 09/30/2011 04:37 PM, Brendan Eich wrote:

since we haven't come up with a way to do 2 and 5 that works,

That's what makes this into a dead end. Worse, by claiming the class syntax you'd be precluding finding a different way that works in the future.

That does not follow. Item 2 is guaranteed shape, item 5 is const instance variable. We can add these later, restricting the syntax for now to exclude anything like 'const' instance variable declarations. This does not preclude adding such extensions later.

# Brendan Eich (14 years ago)

On Oct 1, 2011, at 2:07 AM, Brendan Eich wrote:

On Oct 1, 2011, at 1:57 AM, Waldemar Horwat wrote:

On 09/30/2011 04:37 PM, Brendan Eich wrote:

since we haven't come up with a way to do 2 and 5 that works,

That's what makes this into a dead end. Worse, by claiming the class syntax you'd be precluding finding a different way that works in the future.

That does not follow. Item 2 is guaranteed shape,

Also, const class can be supported now by the desugaring I gave, and it does guarantee the shape of the sealed instances.

# Erik Arvidsson (14 years ago)

I believe that we have a solid proposal even without 2 and 5.

  1. Ensure the shape of the instance

In most cases this is not an issue. Neither ES5 nor Python seems to have suffered too bad without this. There are ways to make this less likely to happen. Always initialize your instance field before calling anything. Setters on the prototype makes this harder but if you really want it you can also set things on your prototype.

class C extends B { constructor(x, y) { this.x = x; this.y = y; super.constructor(); }, x: 0, // To ensure no setter is invoked on a prototype y: 0 }

I know this is pretty painful but I don't consider this to be a show stopper.

  1. Allow const properties

I know this tastes pretty salty but here goes

class Point { constructor(x, y) { Object.defineProperty('x', {value: x}); Object.defineProperty('y', {value: y}); } }

# Axel Rauschmayer (14 years ago)

On Oct 1, 2011, at 2:16 , Juan Ignacio Dopazo wrote:

  1. Don't forget super getters/setters. Those are helpful.

Not sure how often they would be used and whether they need to be part of a minimal feature set.

Isn't it just a matter of referring to the property with "super"?

class Pirate { get name() { return "Captn' " + super.name; } }

Anyway, considering "super" a feature on its own that also works on regular objects is probably better.

  1. This should mean that there must be a way of setting class/static properties

Exactly, that’s one of the pieces.

  1. Encapsulation/privacy should be there somewhere.

The private names proposal should work, as a first solution.

That doesn't solve a very common use case: private subroutines for public methods.

Can you elaborate? Subroutines sound like nested functions.

I think we need to get our creative mindset on and come up with a better name for "static" ASAP before this gets more complicated.

Done. Look here: esdiscuss/2011-September/016892

The section syntax is quite slick. And the name “class” works out nicely, in several roles.

# Waldemar Horwat (14 years ago)

On 09/30/2011 05:07 PM, Brendan Eich wrote:

On 09/30/2011 04:37 PM, Brendan Eich wrote:

since we haven't come up with a way to do 2 and 5 that works,

We can add these later ...

Those two statements you made are in direct contradiction. If there were a way to do it that's satisfactory to the group, we would have found it by now.

 Waldemar
# Brendan Eich (14 years ago)

On Oct 1, 2011, at 3:34 AM, Waldemar Horwat wrote:

On 09/30/2011 05:07 PM, Brendan Eich wrote:

On 09/30/2011 04:37 PM, Brendan Eich wrote:

since we haven't come up with a way to do 2 and 5 that works,

We can add these later ...

Those two statements you made are in direct contradiction.

No, not logically -- you would have to assume something more, like for instance:

If there were a way to do it that's satisfactory to the group, we would have found it by now.

and I didn't make this third statement. It's your assumption.

Just because we haven't found something, doesn't mean we (or some wider group) never will. But I can't disprove it.

What needs demonstration, not assertion, is how minimal classes preclude adding instance variable declaration forms of whatever syntax in the future that address item 5. See my followup about item 2 and const class, which provides the shape guarantee.

But I take it you want to have no classes, in case even minimal classes would somehow paint us into a corner. If that's right, we should cut them from ES6 and get on with the rest of the work.

# Axel Rauschmayer (14 years ago)

I'd argue that my pattern also supports #5 as IDE's can recognize the pattern. An imperative class definition of all that most Smalltalk implementations every had and they generally had fine IDEs.

From what I remember from my Smalltalk days (literally – a 14 day project at school ;-), Smalltalk’s syntax is much more amenable to this kind of thing. Just the fact that if-then-else is a method of boolean objects is brilliant.

In JavaScript, it might be more natural to take the chain block -> function -> object literal one step further and add class literals. I always liked how lightweight things felt in JavaScript (compared to, say, Java or C++). And if we keep object literals minimal, that won’t go away.

# Waldemar Horwat (14 years ago)

On 09/30/2011 06:51 PM, Brendan Eich wrote:

On Oct 1, 2011, at 3:34 AM, Waldemar Horwat wrote:

On 09/30/2011 05:07 PM, Brendan Eich wrote:

On 09/30/2011 04:37 PM, Brendan Eich wrote:

since we haven't come up with a way to do 2 and 5 that works,

We can add these later ...

Those two statements you made are in direct contradiction.

No, not logically -- you would have to assume something more, like for instance:

If there were a way to do it that's satisfactory to the group, we would have found it by now.

and I didn't make this third statement. It's your assumption.

So you're saying we should keep looking?

I feel like you're playing word games. Being satisfactory to the group is an obvious and implied requirement.

There are lots of ways to do classes that satisfy all of 2-5. However, it doesn't matter if those exist if those solutions are not acceptable to the group.

You haven't demonstrated any approach which "we can add later" that would be acceptable to the group. On the contrary, experience from the past two meetings suggests that it does not exist.

 Waldemar
# John J Barton (14 years ago)

On Fri, Sep 30, 2011 at 5:47 PM, Axel Rauschmayer <axel at rauschma.de> wrote:

Isn't it just a matter of referring to the property with "super"? class Pirate {   get name() {     return "Captn' " + super.name;   } }

just trying to understand: how is super different from proto? jjb

# Brendan Eich (14 years ago)

On Oct 1, 2011, at 4:23 AM, Waldemar Horwat wrote:

On 09/30/2011 06:51 PM, Brendan Eich wrote:

On Oct 1, 2011, at 3:34 AM, Waldemar Horwat wrote:

On 09/30/2011 05:07 PM, Brendan Eich wrote:

On 09/30/2011 04:37 PM, Brendan Eich wrote:

since we haven't come up with a way to do 2 and 5 that works,

We can add these later ...

Those two statements you made are in direct contradiction.

No, not logically -- you would have to assume something more, like for instance:

If there were a way to do it that's satisfactory to the group, we would have found it by now.

and I didn't make this third statement. It's your assumption.

So you're saying we should keep looking?

I'm saying the same thing Arv said: we can have useful minimal classes now and not be hostile to const properties in the future, added by some means (possibly already discussed, e.g. the read barrier).

I feel like you're playing word games. Being satisfactory to the group is an obvious and implied requirement.

Who is playing games? You said my two statements contradict each other but they do not. You assume or assert that we can't do minimal classes without going down a bad path for const properties later, but that's not self-evident.

If you could say why minimal classes are future-hostile to const properties, that would help.

There are lots of ways to do classes that satisfy all of 2-5. However, it doesn't matter if those exist if those solutions are not acceptable to the group.

I know, I was ok with a read barrier for properties to enforce a temporal dead zone for const properties. Others were not ok with whatever overhead that might add to all property reads.

But who says we need to solve 5 now? A number of us are saying we can defer it.

You haven't demonstrated any approach which "we can add later" that would be acceptable to the group. On the contrary, experience from the past two meetings suggests that it does not exist.

I don't have to make the future come true now to believe that minimal classes are useful and not future-hostile. You haven't demonstrated that minimal classes are not useful enough to be worth doing without const properties.

"Classes desugared by hand" have been done for years in JS without any kind of non-writable property definition primitive, let alone one you can't read as undefined before it has been initialized. These are use-cases for classes as sugar. Why can't we specify classes as sugar for these use-cases and defer const properties?

I'm not going to keep going around and around on this whole argument, though. If, even without some kind of evidence or demonstration, you're convinced that minimal classes are future-hostile, I give -- we can defer classes entirely and get on with other work.

# Brendan Eich (14 years ago)

On Oct 1, 2011, at 5:24 AM, Brendan Eich wrote:

On Oct 1, 2011, at 4:23 AM, Waldemar Horwat wrote:

There are lots of ways to do classes that satisfy all of 2-5. However, it doesn't matter if those exist if those solutions are not acceptable to the group.

I know, I was ok with a read barrier for properties to enforce a temporal dead zone for const properties. Others were not ok with whatever overhead that might add to all property reads.

But who says we need to solve 5 now? A number of us are saying we can defer it.

And here's how this might then play out: some of us who do value const properties prototype them as an extension to minimal classes, and we demonstrate in fast VMs that the read barrier cost is negligible. The rest of the group is convinced, and we add const properties later (next edition, this edition if in time, doesn't matter).

By insisting on solving all of 2-5 up front, when demonstration of low-enough cost imposed by implementation of 5 is required by some in the group, you guarantee no classes. By letting minimal classes proceed, you might get const properties too.

I can't guarantee this, but I can guarantee if we keep going in circles we'll have no classes.

You might argue that implementors can prototype solutions for 2-5 already, but I think no one will risk it without more consensus in the committee. So even if minimal classes is just an agreement for implementors, it would help us get to consensus on the solution for 5. Does this make sense?

# Oliver Hunt (14 years ago)

Imagine

class Foo { function name() { return "foo" } }

class Bar : Foo { // s/:/extends or whatever function name() { super.name(); } }

class Wibble : Bar { function name() { super.name(); } }

the call to super.name() essentially desugars to:

this.proto.name.call(this, ...)

But this.proto will be the same everywhere, so someWibble.name() will infinitely recurse when it reaches Bar::name

Hope this helps.

# John J Barton (14 years ago)

On Fri, Sep 30, 2011 at 9:01 PM, Oliver Hunt <oliver at apple.com> wrote:

Imagine

class Foo {   function name() {         return "foo"   } }

class Bar : Foo { // s/:/extends or whatever   function name() {         super.name();   } }

class Wibble : Bar {   function name() {         super.name();   } }

the call to super.name() essentially desugars to:

this.proto.name.call(this, ...)

But this.proto will be the same everywhere, so someWibble.name() will infinitely recurse when it reaches Bar::name

Hope this helps.

Eventually, yes thanks. (Assuming I am translating the stuff above correctly).

If we try to use this.proto in some JS like the stuff above we get a call to missing method.

someWibble.name() calls Wibblle.prototype.name with |this| bound to someWibble. someWibble.proto points to Bar.prototype that's good. But Bar.prototype.proto points to Object, not Foo. So when we try to recurse in bar with return this.proto.name(); then |this| is a Bar.prototype, not the result of new Bar().

Of course we just use Bar.prototype.name = function() { return Foo.prototype.name() } now instead.

jjb

# Waldemar Horwat (14 years ago)

On 09/30/2011 08:43 PM, Brendan Eich wrote:

On Oct 1, 2011, at 5:24 AM, Brendan Eich wrote:

On Oct 1, 2011, at 4:23 AM, Waldemar Horwat wrote:

There are lots of ways to do classes that satisfy all of 2-5. However, it doesn't matter if those exist if those solutions are not acceptable to the group.

I know, I was ok with a read barrier for properties to enforce a temporal dead zone for const properties. Others were not ok with whatever overhead that might add to all property reads.

But who says we need to solve 5 now? A number of us are saying we can defer it.

And here's how this might then play out: some of us who do value const properties prototype them as an extension to minimal classes, and we demonstrate in fast VMs that the read barrier cost is negligible. The rest of the group is convinced, and we add const properties later (next edition, this edition if in time, doesn't matter).

By insisting on solving all of 2-5 up front, when demonstration of low-enough cost imposed by implementation of 5 is required by some in the group, you guarantee no classes. By letting minimal classes proceed, you might get const properties too.

I can't guarantee this, but I can guarantee if we keep going in circles we'll have no classes.

You might argue that implementors can prototype solutions for 2-5 already, but I think no one will risk it without more consensus in the committee. So even if minimal classes is just an agreement for implementors, it would help us get to consensus on the solution for 5. Does this make sense?

Given that the choice seems to be between doing potentially future-hostile classes (where we'd bemoan the water under the bridge when trying to solve 2-6) and omitting classes altogether, C.A.R. Hoare's advice (as quoted by Bill Frantz) to defer classes would be the wiser thing to do. The standard is not a place for poorly understood features.

 Waldemar
# Brendan Eich (14 years ago)

On Oct 3, 2011, at 8:26 PM, Waldemar Horwat wrote:

On 09/30/2011 08:43 PM, Brendan Eich wrote:

On Oct 1, 2011, at 5:24 AM, Brendan Eich wrote:

On Oct 1, 2011, at 4:23 AM, Waldemar Horwat wrote:

There are lots of ways to do classes that satisfy all of 2-5. However, it doesn't matter if those exist if those solutions are not acceptable to the group.

I know, I was ok with a read barrier for properties to enforce a temporal dead zone for const properties. Others were not ok with whatever overhead that might add to all property reads.

But who says we need to solve 5 now? A number of us are saying we can defer it.

And here's how this might then play out: some of us who do value const properties prototype them as an extension to minimal classes, and we demonstrate in fast VMs that the read barrier cost is negligible. The rest of the group is convinced, and we add const properties later (next edition, this edition if in time, doesn't matter).

By insisting on solving all of 2-5 up front, when demonstration of low-enough cost imposed by implementation of 5 is required by some in the group, you guarantee no classes. By letting minimal classes proceed, you might get const properties too.

I can't guarantee this, but I can guarantee if we keep going in circles we'll have no classes.

You might argue that implementors can prototype solutions for 2-5 already, but I think no one will risk it without more consensus in the committee. So even if minimal classes is just an agreement for implementors, it would help us get to consensus on the solution for 5. Does this make sense?

Given that the choice seems to be between doing potentially future-hostile classes (where we'd bemoan the water under the bridge when trying to solve 2-6) and omitting classes altogether, C.A.R. Hoare's advice (as quoted by Bill Frantz) to defer classes would be the wiser thing to do. The standard is not a place for poorly understood features.

I agree.

If we had agreed on the read barrier, I think we would probably have consensus on classes. It's interesting that Oliver (Apple) and I (at least, among Mozillans) agreed. Other implementations were not represented in my view. Take this for what it's worth.

# Erik Arvidsson (14 years ago)

On Mon, Oct 3, 2011 at 12:26, Waldemar Horwat <waldemar at google.com> wrote:

On 09/30/2011 08:43 PM, Brendan Eich wrote:

On Oct 1, 2011, at 5:24 AM, Brendan Eich wrote:

On Oct 1, 2011, at 4:23 AM, Waldemar Horwat wrote:

There are lots of ways to do classes that satisfy all of 2-5.  However, it doesn't matter if those exist if those solutions are not acceptable to the group.

I know, I was ok with a read barrier for properties to enforce a temporal dead zone for const properties. Others were not ok with whatever overhead that might add to all property reads.

But who says we need to solve 5 now? A number of us are saying we can defer it.

And here's how this might then play out: some of us who do value const properties prototype them as an extension to minimal classes, and we demonstrate in fast VMs that the read barrier cost is negligible. The rest of the group is convinced, and we add const properties later (next edition, this edition if in time, doesn't matter).

By insisting on solving all of 2-5 up front, when demonstration of low-enough cost  imposed by implementation of 5 is required by some in the group, you guarantee no classes. By letting minimal classes proceed, you might get const properties too.

I can't guarantee this, but I can guarantee if we keep going in circles we'll have no classes.

You might argue that implementors can prototype solutions for 2-5 already, but I think no one will risk it without more consensus in the committee. So even if minimal classes is just an agreement for implementors, it would help us get to consensus on the solution for 5. Does this make sense?

Given that the choice seems to be between doing potentially future-hostile classes (where we'd bemoan the water under the bridge when trying to solve 2-6) and omitting classes altogether, C.A.R. Hoare's advice (as quoted by Bill Frantz) to defer classes would be the wiser thing to do.  The standard is not a place for poorly understood features.

I agree and that is why I'm arguing for the even simpler classes proposal.

By not providing syntax we are continuing to encourage a million incompatible "class" libraries.

# Alex Russell (14 years ago)

On Oct 3, 2011, at 9:57 PM, Brendan Eich wrote:

On Oct 3, 2011, at 8:26 PM, Waldemar Horwat wrote:

On 09/30/2011 08:43 PM, Brendan Eich wrote:

On Oct 1, 2011, at 5:24 AM, Brendan Eich wrote:

On Oct 1, 2011, at 4:23 AM, Waldemar Horwat wrote:

There are lots of ways to do classes that satisfy all of 2-5. However, it doesn't matter if those exist if those solutions are not acceptable to the group.

I know, I was ok with a read barrier for properties to enforce a temporal dead zone for const properties. Others were not ok with whatever overhead that might add to all property reads.

But who says we need to solve 5 now? A number of us are saying we can defer it.

And here's how this might then play out: some of us who do value const properties prototype them as an extension to minimal classes, and we demonstrate in fast VMs that the read barrier cost is negligible. The rest of the group is convinced, and we add const properties later (next edition, this edition if in time, doesn't matter).

By insisting on solving all of 2-5 up front, when demonstration of low-enough cost imposed by implementation of 5 is required by some in the group, you guarantee no classes. By letting minimal classes proceed, you might get const properties too.

I can't guarantee this, but I can guarantee if we keep going in circles we'll have no classes.

You might argue that implementors can prototype solutions for 2-5 already, but I think no one will risk it without more consensus in the committee. So even if minimal classes is just an agreement for implementors, it would help us get to consensus on the solution for 5. Does this make sense?

Given that the choice seems to be between doing potentially future-hostile classes (where we'd bemoan the water under the bridge when trying to solve 2-6) and omitting classes altogether, C.A.R. Hoare's advice (as quoted by Bill Frantz) to defer classes would be the wiser thing to do. The standard is not a place for poorly understood features.

I agree.

If we had agreed on the read barrier, I think we would probably have consensus on classes. It's interesting that Oliver (Apple) and I (at least, among Mozillans) agreed.

So you're throwing out things on which basis? Syntax or read barrier? We got mild push-back from MSFT on complexity WRT read barrier. Others seemed to be OK with it. Oliver didn't like the declarative syntax.

Other implementations were not represented in my view. Take this for what it's worth.

Will feel free to take it as "not the consensus view" then.

# Brendan Eich (14 years ago)

On Oct 4, 2011, at 12:16 AM, Alex Russell wrote:

On Oct 3, 2011, at 9:57 PM, Brendan Eich wrote:

On Oct 3, 2011, at 8:26 PM, Waldemar Horwat wrote:

On 09/30/2011 08:43 PM, Brendan Eich wrote:

On Oct 1, 2011, at 5:24 AM, Brendan Eich wrote:

On Oct 1, 2011, at 4:23 AM, Waldemar Horwat wrote:

There are lots of ways to do classes that satisfy all of 2-5. However, it doesn't matter if those exist if those solutions are not acceptable to the group.

I know, I was ok with a read barrier for properties to enforce a temporal dead zone for const properties. Others were not ok with whatever overhead that might add to all property reads.

But who says we need to solve 5 now? A number of us are saying we can defer it.

And here's how this might then play out: some of us who do value const properties prototype them as an extension to minimal classes, and we demonstrate in fast VMs that the read barrier cost is negligible. The rest of the group is convinced, and we add const properties later (next edition, this edition if in time, doesn't matter).

By insisting on solving all of 2-5 up front, when demonstration of low-enough cost imposed by implementation of 5 is required by some in the group, you guarantee no classes. By letting minimal classes proceed, you might get const properties too.

I can't guarantee this, but I can guarantee if we keep going in circles we'll have no classes.

You might argue that implementors can prototype solutions for 2-5 already, but I think no one will risk it without more consensus in the committee. So even if minimal classes is just an agreement for implementors, it would help us get to consensus on the solution for 5. Does this make sense?

Given that the choice seems to be between doing potentially future-hostile classes (where we'd bemoan the water under the bridge when trying to solve 2-6) and omitting classes altogether, C.A.R. Hoare's advice (as quoted by Bill Frantz) to defer classes would be the wiser thing to do. The standard is not a place for poorly understood features.

I agree.

If we had agreed on the read barrier, I think we would probably have consensus on classes. It's interesting that Oliver (Apple) and I (at least, among Mozillans) agreed.

So you're throwing out things on which basis?

Why are you trying to pick a fight with me?

Waldemar just suggested following Tony Hoare's advice. Do you disagree or not? If not, why not? Anyway, don't personalize this to me. If you want to pick a personal fight, start with Waldemar.

Syntax or read barrier?

Neither class syntax nor const inclusion (item 5) and the read barrier to detect use of a const instance variable before it has been initialized reached consensus. This has nothing to do with me or what I just wrote in the message to which you replied in a fight-y way.

That we did not reach consensus is a fact. Please deal with it constructively. I am trying to. I see only two routes: agree quickly on syntax and semantics of some minimized classes design, or cut classes for now, which means pending new information, from ES6.

We got mild push-back from MSFT on complexity WRT read barrier. Others seemed to be OK with it. Oliver didn't like the declarative syntax.

Oliver liked declarative syntax that put instance variable declaration in the class body, not (in arbitrary control flow) in the constructor body.

Oliver and many others do not like the current wiki'ed proposal's placement of public instance variable declarations in the constructor body. At the meeting we talked about trying to restrict where in that body such declarations might occur, and in what order with respect to super() or an initialized declaration boundary -- we failed to reach consensus on anything.

Other implementations were not represented in my view. Take this for what it's worth.

Will feel free to take it as "not the consensus view" then.

Right -- we have no consensus, not on what Oliver and I (and I think Waldemar) want. We also do not have consensus on alterantive classes designs -- including for the public-declarations-in-constructor-body proposal you seem to be advancing here, by passive-aggressive impliciation.

We Clearly Do Not Have Consensus On Classes. :-|

# Brendan Eich (14 years ago)

On Oct 3, 2011, at 11:43 PM, Erik Arvidsson wrote:

On Mon, Oct 3, 2011 at 12:26, Waldemar Horwat <waldemar at google.com> wrote:

Given that the choice seems to be between doing potentially future-hostile classes (where we'd bemoan the water under the bridge when trying to solve 2-6) and omitting classes altogether, C.A.R. Hoare's advice (as quoted by Bill Frantz) to defer classes would be the wiser thing to do. The standard is not a place for poorly understood features.

I agree and that is why I'm arguing for the even simpler classes proposal.

By not providing syntax we are continuing to encourage a million incompatible "class" libraries.

Waldemar is concerned that minimal classes now will prevent adding const instance variable declarations later, I think. Possibly also shape guarantees (if not served by 'const class' now). Waldemar, is that right?

If so we are at an impasse. To get past it, we would need to agree on declarative syntax and semantics preventing use before initialization. We can try to do that again, see if anyone has changed minds.

# Erik Arvidsson (14 years ago)

On Mon, Oct 3, 2011 at 17:25, Brendan Eich <brendan at mozilla.com> wrote:

If so we are at an impasse. To get past it, we would need to agree on declarative syntax and semantics preventing use before initialization. We can try to do that again, see if anyone has changed minds.

I don't agree that we need to agree on a semantic that prevents use before initialization for ES.next. Like I've previously stated both Python and ES5 gets by without this.

The proposal we were talking about (last hour of the F2F) was something even simpler than what we have at the wiki. It did involve using object literal for the class body and no special form for the constructor. This makes things simpler but the main downside with that is that it is not as easily extended to add const and whatever feature we might want in the future.

# Russell Leggett (14 years ago)

On Mon, Oct 3, 2011 at 9:37 PM, Erik Arvidsson <erik.arvidsson at gmail.com> wrote:

On Mon, Oct 3, 2011 at 17:25, Brendan Eich <brendan at mozilla.com> wrote:

If so we are at an impasse. To get past it, we would need to agree on declarative syntax and semantics preventing use before initialization. We can try to do that again, see if anyone has changed minds.

I don't agree that we need to agree on a semantic that prevents use before initialization for ES.next. Like I've previously stated both Python and ES5 gets by without this.

The proposal we were talking about (last hour of the F2F) was something even simpler than what we have at the wiki. It did involve using object literal for the class body and no special form for the constructor. This makes things simpler but the main downside with that is that it is not as easily extended to add const and whatever feature we might want in the future.

I don't want to be pushy, so this is the last time that I'll mention it, but if we can create something using the <| operator that can basically do what has been discussed for the simplest class literal, but without the class keyword, it would leave the door open for a future spec. This was was my proposal from before:

   const ClassName = SuperClass <| {
           constructor(/*constructor parameters */) {
              //constructor body
              super.constructor(/*arguments to super constructor */);
              this.{
                //per instance property definitions
              }
           }
           method1(){ return super.method1(); }
           method2(){}
           prop1:"Properties unlikely, but allowed"
   }.{
           //class properties
           staticMethod(){}
   };

Where SuperClass is a constructor function, so LHS: function, RHS: object literal == class without the keyword. In the most common case, there wouldn't be any "static" methods/properties so you normally wouldn't even need the slightly awkward use of '.{' here.

# Brendan Eich (14 years ago)

On Oct 4, 2011, at 2:37 AM, Erik Arvidsson wrote:

On Mon, Oct 3, 2011 at 17:25, Brendan Eich <brendan at mozilla.com> wrote:

If so we are at an impasse. To get past it, we would need to agree on declarative syntax and semantics preventing use before initialization. We can try to do that again, see if anyone has changed minds.

I don't agree that we need to agree on a semantic that prevents use before initialization for ES.next. Like I've previously stated both Python and ES5 gets by without this.

Right, but IIRC at least Waldemar disagrees, or at least (more below) wants to future-proof carefully.

The proposal we were talking about (last hour of the F2F) was something even simpler than what we have at the wiki. It did involve using object literal for the class body and no special form for the constructor. This makes things simpler

For us, perhaps. For users, less so.

If we add class syntax, it's not hard to make the class body it's own thing.

I think this is better for usability than recycling ObjectLiteral. I acknowledge your point that mixing declarations in the body as directly contained elements suggests methods close over them. My most-minimal solution, proposed on the list and IIRC at the meeting, is to forbid non-method declarations in class bodies, outside of class: sections if we choose to support that.

but the main downside with that is that it is not as easily extended to add const and whatever feature we might want in the future.

That does seem to ask for trouble, on the very basis that led to Hoare being invoked.

# Brendan Eich (14 years ago)

On Oct 4, 2011, at 5:43 AM, Russell Leggett wrote:

I don't want to be pushy, so this is the last time that I'll mention it, but if we can create something using the <| operator that can basically do what has been discussed for the simplest class literal,

I think you're barking up several wrong trees. But yes, Allen proposed <| and .{ to help make class-like syntax lighter-weight (if more punctuated), without having real class syntax. Unfortunately any such poor-person's "class unsyntax" will remain error prone (easy to leave out or misorder a sub-expression) and slightly verbose (compared to just-so class syntax).

but without the class keyword, it would leave the door open for a future spec. This was was my proposal from before:

  const ClassName = SuperClass <| {
          constructor(/*constructor parameters */) {
             //constructor body
             super.constructor(/*arguments to super constructor */);
             this.{
               //per instance property definitions
             }
          }
          method1(){ return super.method1(); }
          method2(){}
          prop1:"Properties unlikely, but allowed"
  }.{
          //class properties
          staticMethod(){}

These go on the constructor, not the prototype. But to the left of .{ is an object (literal), not a function.

If you are assuming .{ binds to the result of evaluating the unparenthesized <| expression to its left, then there's a precedence problem.

  };

Where SuperClass is a constructor function, so LHS: function, RHS: object literal == class without the keyword.

This completely breaks <| as the set-proto-at-birth operator.

Allen's pattern,

const px = Name.create(‘x’), py = Name.create(‘y’);

let Point = Base <| function (x, y) { super(); this[px] = x, this[py] = y; this.r = function() { return Math.sqrt(xx + yy); } }.prototype.{ get x() { return this[px]; }, get y() { return this[py]; }, proto_r() { return Math.sqrt(this[px] * this[px] + this[py] * this[py]); }, equals(p) { return this[px] === p[px] && this[py] === p[py]; } }.constructor.{ allPoints: [] // class “static” property! } works without hardcoding <| for class inheritance in the way you suggest, and without precedence shifts or implicit switch to constructor from prototype, or prototype from constructor.

# Russell Leggett (14 years ago)

On Tue, Oct 4, 2011 at 3:44 AM, Brendan Eich <brendan at mozilla.com> wrote:

On Oct 4, 2011, at 5:43 AM, Russell Leggett wrote:

I don't want to be pushy, so this is the last time that I'll mention it, but if we can create something using the <| operator that can basically do what has been discussed for the simplest class literal,

I think you're barking up several wrong trees. But yes, Allen proposed <| and .{ to help make class-like syntax lighter-weight (if more punctuated), without having real class syntax. Unfortunately any such poor-person's "class unsyntax" will remain error prone (easy to leave out or misorder a sub-expression) and slightly verbose (compared to just-so class syntax).

That's why I was trying to make it less error prone and verbose.

but without the class keyword, it would leave the door open for a future spec. This was was my proposal from before:

const ClassName = SuperClass <| {               constructor(/*constructor parameters */) {                  //constructor body                  super.constructor(/*arguments to super constructor */);                  this.{                    //per instance property definitions                  }               }               method1(){ return super.method1(); }               method2(){}               prop1:"Properties unlikely, but allowed"       }.{               //class properties               staticMethod(){}

These go on the constructor, not the prototype. But to the left of .{ is an object (literal), not a function. If you are assuming .{ binds to the result of evaluating the unparenthesized <| expression to its left, then there's a precedence problem.

I was assuming <| was evaluated first because that's how I read Allen's pattern working as well. I'm wondering how Allen's even works now. My thought was that if func <| obj => func, then .{ would go on

the resulting function.

};

Where SuperClass is a constructor function, so LHS: function, RHS: object literal == class without the keyword.

This completely breaks <| as the set-proto-at-birth operator. Allen's pattern,

const px = Name.create(‘x’), py = Name.create(‘y’);

let Point = Base <| function (x, y) {   super();   this[px] = x, this[py] = y;   this.r = function() { return Math.sqrt(xx + yy); } }.prototype.{   get x()   { return this[px]; },   get y()   { return this[py]; },   proto_r() { return Math.sqrt(this[px] * this[px] +                                this[py] * this[py]); },   equals(p) { return this[px] === p[px] &&                      this[py] === p[py]; } }.constructor.{   allPoints: []  // class “static” property! }

works without hardcoding <| for class inheritance in the way you suggest, and without precedence shifts or implicit switch to constructor from prototype, or prototype from constructor.

I'm puzzling at Allen's pattern then, because I don't see how it would work either. If, as you're saying, the .{ has higher precedence, then it seems to me the entire RHS would be evaluated first complete with adjusted prototype and class members, and then handed to the <| operator. I didn't think that was supposed to work, because the RHS was supposed to be a literal, but the .{ are modifying it first, which doesn't seem right.

Also, I guess I'm not entirely sure how this would work in terms of making the prototypes/constructors all working. From what you're saying, Allen's pattern basically does this:

const Foo = ...//setup base class with constructor and methods
const Bar = ...//setup child class with constructor and methods
//now do __proto__ magic
Bar.__proto__ = Foo;
new Bar();//Does this really do all the wiring I need?

Having tried that, it doesn't seem to do much of anything, so I guess I assumed there was a little more work going on under the hood using <| on functions. Or is it that it only works because of the call to super?

Anyway, I don't want to waste your time with my confusion, I'll dig into the docs a little more to see if I missed something, and I really do hope you come to some agreement on something for classes. Until there is some agreed on form, I suspect we will deal with many incompatible libraries for a while.

# Allen Wirfs-Brock (14 years ago)

On Oct 4, 2011, at 12:44 AM, Brendan Eich wrote:

On Oct 4, 2011, at 5:43 AM, Russell Leggett wrote:

I don't want to be pushy, so this is the last time that I'll mention it, but if we can create something using the <| operator that can basically do what has been discussed for the simplest class literal,

I think you're barking up several wrong trees. But yes, Allen proposed <| and .{ to help make class-like syntax lighter-weight (if more punctuated), without having real class syntax. Unfortunately any such poor-person's "class unsyntax" will remain error prone (easy to leave out or misorder a sub-expression) and slightly verbose (compared to just-so class syntax).

Brendan, It sounds like you may have missed this alternative from the "Minor extension to Set Literal Prototype..." thread that eliminates the mis-ordered or left-out constructor clause problem.

On Oct 2, 2011, at 12:52 PM, Allen Wirfs-Brock wrote:

...

# Brendan Eich (14 years ago)

On Oct 4, 2011, at 6:17 AM, Russell Leggett wrote:

That's why I was trying to make it less error prone and verbose.

Understood, but we should not "scenario solve" with ad-hoc, compound additions. We seek orthogonal primitives that compose well, and the harder scenarios ideally desugar.

If you are assuming .{ binds to the result of evaluating the unparenthesized <| expression to its left, then there's a precedence problem.

I was assuming <| was evaluated first because that's how I read Allen's pattern working as well. I'm wondering how Allen's even works now.

The <| operator must not take an arbitrary expression on its right, because that allows for mutation of [[Prototype]] on an escaped "old-born" object.

But <| could take literal forms and "concatenations" of literal forms via .prototype.{...}.constructor, e.g.

Or <| binds tighter than . (but I think this would be a mistake).

My thought was that if func <| obj => func, then .{ would go on the resulting function.

No, func <| {...} makes an object whose [[Prototype]] is func.

# Brendan Eich (14 years ago)

On Oct 4, 2011, at 8:35 AM, Allen Wirfs-Brock wrote:

On Oct 4, 2011, at 12:44 AM, Brendan Eich wrote:

On Oct 4, 2011, at 5:43 AM, Russell Leggett wrote:

I don't want to be pushy, so this is the last time that I'll mention it, but if we can create something using the <| operator that can basically do what has been discussed for the simplest class literal,

I think you're barking up several wrong trees. But yes, Allen proposed <| and .{ to help make class-like syntax lighter-weight (if more punctuated), without having real class syntax. Unfortunately any such poor-person's "class unsyntax" will remain error prone (easy to leave out or misorder a sub-expression) and slightly verbose (compared to just-so class syntax).

Brendan, It sounds like you may have missed this alternative from the "Minor extension to Set Literal Prototype..." thread that eliminates the mis-ordered or left-out constructor clause problem.

Care to summarize? I was traveling and skimming some of the long messages in that thread.

Also, I fear we are pattern-mongering prematurely. We want something like <| for sure (exact spelling needs work IMHO). We could have just Object.extend instead of .{...} and some on the committee objected to the mutating nature of a variant on dot -- they wanted .= at least -- but since there is no requirement that the RHS be a literal, Object.extend is enough.

IINM .{...} is trying to make a shorthand, not just for its own sake but to make a class-like pattern and reduce the perceived need for classes as sugar. I think that's not well-motivated at this point.