extends keyword instead of <superclass ... >

# Dmitry A. Soshnikov (14 years ago)

Just a small note on strawman:super_in_object_initialisers (reading currently)

Why not just to use already reserved extends keyword for specifying a superclass? These XML-like braces looks not so elegant.

Coffee users the same classes syntactic sugar as planned for ES6: jashkenas.github.com/coffee-script/#classes and also reuses familiar (for all?) extends keyword of ES3.

P.S.:

This (XML-like syntax) relates also to other meta-properties/attributes in object initialisers. I prefer to use @ instead:

let foo = { @proto: bar, @sealed, move: # (x, y) { ... } };

Though, again, e.g. Coffee (on Ruby's style) users @ for this. Does JS need this sugar for this? In this case (since # is planned to be used actively) meta-properties can be:

let foo = { #proto: bar, #sealed, move: # (x, y) { ... } };

Dmitry.

# Juan Ignacio Dopazo (14 years ago)

On Sat, Mar 26, 2011 at 6:31 PM, Dmitry A. Soshnikov < dmitry.soshnikov at gmail.com> wrote:

Why not just to use already reserved extends keyword for specifying a superclass? These XML-like braces looks not so elegant.

I asked this question a couple of days ago. The answer is quite simple. Object initializer extensions are more than just constructor syntax. They allow you to create complex objects without all the hassle in (function(){}()). So the constructor syntax was made this way to be consistent with object initializer syntax.

Juan

# Dmitry A. Soshnikov (14 years ago)

On 27.03.2011 22:33, Juan Ignacio Dopazo wrote:

On Sat, Mar 26, 2011 at 6:31 PM, Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com <mailto:dmitry.soshnikov at gmail.com>> wrote:

Why not just to use already reserved `extends` keyword for
specifying a superclass? These XML-like braces looks not so elegant.

I asked this question a couple of days ago.

Really? Sorry, seems I missed, I'll take a look (a direct link will be appreciated).

The answer is quite simple. Object initializer extensions are more than just constructor syntax. They allow you to create complex objects without all the hassle in (function(){}()). So the constructor syntax was made this way to be consistent with object initializer syntax.

There can be exception for at least class definition since extends, repeat, is/was already reserved and familiar to many programmers.

Dmitry.

# Juan Ignacio Dopazo (14 years ago)

Sure, it was discussed in this thread esdiscuss/2011-March/012963

esdiscuss/2011-March/012963Juan

# Brendan Eich (14 years ago)

On Mar 27, 2011, at 11:33 AM, Juan Ignacio Dopazo wrote:

On Sat, Mar 26, 2011 at 6:31 PM, Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com> wrote: Why not just to use already reserved extends keyword for specifying a superclass? These XML-like braces looks not so elegant.

I asked this question a couple of days ago. The answer is quite simple. Object initializer extensions are more than just constructor syntax. They allow you to create complex objects without all the hassle in (function(){}()). So the constructor syntax was made this way to be consistent with object initializer syntax.

The meta syntax (e.g., var obj = {<proto: p>, key: val}) is not inconsistent with initialisers, it's an extension to them. But it's not the only self-consistent extension. Dmitry mentioned letting the meta properties appear to be regular key: value pairs but distinguishing the keys with @ or #.

Allen makes the point that class D extends B {...} may look too much like languages where it means something quite different. That's true, but I am not sure JS should be so special that it seems to mix bits of those languages into one (of several conceivable) extensions to initialiser syntax:

class D {<superclass B>};

Whatever you think of the meta section, the leading 'class D {' telegraphs influence from, and just familiarity with, those other languages. And I don't think that is a bad thing even if semantics differ.

Just changing 'class' to 'constructor' (adding a new perhaps-only-contextually reserved word, and a long one at that) is not the issue. Sure, doing that would in a text-only way make the syntax not look like any other language, exactly. But ignore the skin: the bones look familiar, going back to C structs.

Now add some notion of inheritance and you're following the C++ "body plan".

Do we really need to look different yet still have bones that look so familiar? I am not convinced.

The strongest case for extending initialiser syntax is that it is "nearly declarative" today. That suggests strongly making the extension fully declarative, i.e., not usable as an expression. And that, all else equal (never is, but:) says to consider class D extends B {...}. My 2 cents.

# David Herman (14 years ago)

Allen makes the point that class D extends B {...} may look too much like languages where it means something quite different.

Yeah, I just don't buy the argument that having different semantics should lead to different syntax: it proves too much. By definition, JS has a different semantics from other languages -- it's a different language. Are we obliged to use radically different syntax for the whole of JS? Certainly not. The point of using familiar syntax is to call out the similarities, but there will always be differences.

Whatever you think of the meta section, the leading 'class D {' telegraphs influence from, and just familiarity with, those other languages. And I don't think that is a bad thing even if semantics differ.

Just changing 'class' to 'constructor' (adding a new perhaps-only-contextually reserved word, and a long one at that) is not the issue. Sure, doing that would in a text-only way make the syntax not look like any other language, exactly. But ignore the skin: the bones look familiar, going back to C structs.

Now add some notion of inheritance and you're following the C++ "body plan".

Do we really need to look different yet still have bones that look so familiar? I am not convinced.

The strongest case for extending initialiser syntax is that it is "nearly declarative" today. That suggests strongly making the extension fully declarative, i.e., not usable as an expression. And that, all else equal (never is, but:) says to consider class D extends B {...}. My 2 cents.

+1

I'm afraid the more radically different syntax just doesn't pass the smell test. Especially given that, as you suggest, the point is to give declarative syntax to what has always been the JS analog of the single inheritance patterns from the C++/Java world.

# Allen Wirfs-Brock (14 years ago)

Consistently extending object literals into "class" literals was one of the goals of this particular design but that goal isn't a requirement. At the latest TC39 meeting some participants expressed an opinion that the prototype specification for an object literal should go outside of the brackets. strawman:proto_operator presents one possible design for that approach. If object literals designs evolve in this or other directions then it may be less important to maintain consistency between object literals and class declarations.

Another thing I haven't seen mentioned on this thread, is that my class declaration proposal supported both proto: and superclass: as alternatives metaproperties with different semantics. There seems to be valid use cases for each. It may not be necessarily to have both but in exploring alternatives that possibility should continue to be considered.

On Mar 27, 2011, at 1:05 PM, Brendan Eich wrote:

Do we really need to look different yet still have bones that look so familiar? I am not convinced.

The strongest case for extending initialiser syntax is that it is "nearly declarative" today. That suggests strongly making the extension fully declarative, i.e., not usable as an expression. And that, all else equal (never is, but:) says to consider class D extends B {...}. My 2 cents.

I don't think "declarative" should not be equated with "not usable as an expression " or introduces a lexical binding. When I say declarative I mean a non-constructive (or non-imperative) definition of the desired value. 3 is declarative, 2+1 isn't. Some people describe this difference as "what, not how". Both function declarations and function expressions are both declarative definitions of functions even though one creates a local binding and the other can be used as an expression. In my proposal, the same distinction was applied to class definitions. There was both a statement and expression form, largely for consistency with functions. I don't think this distinction has much, at all, to do with the syntax for specifying the superclass or prototype.

# Brendan Eich (14 years ago)

all good points. A few thoughts below, interspersed.

On Mar 27, 2011, at 4:39 PM, Allen Wirfs-Brock wrote:

At the latest TC39 meeting some participants expressed an opinion that the prototype specification for an object literal should go outside of the brackets. strawman:proto_operator presents one possible design for that approach.

That strawman is essentially functional record update for setting proto AKA [[Prototype]], although it reads backwards compared to FRU in ML-family languages. Those languages put the update on the right, often linked using 'with' (hah!).

As you note in the strawman, this enables proto-presetting for a number of built-in classes, not just Object and Array.

I'm warming up, modulo the backward sense of the connective (also, you need a [no LineTerminator here] to the left of ''proto'' in the productions). Thinking outside the box, indeed.

Dmitry's suggestion of #proto or a similar sigil-distinguished name makes me wonder, though: could we have FRU via the spread operator and proto presetting without adding a funky [no LineTerminator here] proto infix operator?

var original_o = ...; var o = {#proto: p, ...original_o};

Given this, if you don't have an original_o to specialize (say, a RegExp or other built-in that can't be created via an initialiser), then of course

var o = {#proto: p, key: val, ...};

should be supported.

So, infix operators or magic property names?

For classes, at any rate, it seems to me that On the Outside and Declarative may be at odds, since initialisers are expressions. More below on why.

The strongest case for extending initialiser syntax is that it is "nearly declarative" today. That suggests strongly making the extension fully declarative, i.e., not usable as an expression. And that, all else equal (never is, but:) says to consider class D extends B {...}. My 2 cents.

I don't think "declarative" should not be equated with "not usable as an expression " or introduces a lexical binding.

It's not clear that classes need to bind their names, or even need names. I believe that some on TC39 want non-generative, nominal run-time types via classes. Others may want generative expressions.

The consistent #1 request from users that I've heard over the years, most recently in my blog comments, has been for a declarative form of JS's prototypal object-oriented pattern. This conventionally means a new binding. It's generative if you nest it in an outer function, which is not a problem, but usually it is not nested, or else nested only in an outer IIFE (aka the module pattern).

Both function declarations and function expressions are both declarative definitions of functions

Here is a bit of a nit, but calling both declarative even though one is an expression, not a declaration, sounds like a contradiction, or a redefinition of "declarat{ive,ion}".

More substantive: if we want to enable optimizations and conveniences via syntax for the prototypal pattern, then declarations win, especially if they come with some ahead-of-time semantics, as top-level function declarations evaluated in a compile-and-go scenario do. Thanks to the order of function and then var declaration processing when entering an execution context, such a scenario supports the compiler pre-binding top-level functions.

You're right that expressed as well as declared (whether top-level or not) forms could be supported. The question for classes is whether we would ever want an anonymous or named class expression. Just because 'function' can do that does not mean that 'class' must.

I'd love to hear from Waldemar and MarkM on this point, since I suspect they have something (or things, not necessarily the same) in mind.

even though one creates a local binding and the other can be used as an expression. In my proposal, the same distinction was applied to class definitions. There was both a statement and expression form, largely for consistency with functions. I don't think this distinction has much, at all, to do with the syntax for specifying the superclass or prototype.

Agreed.

# Allen Wirfs-Brock (14 years ago)

On Mar 27, 2011, at 10:31 PM, Brendan Eich wrote:

That strawman is essentially functional record update for setting proto AKA [[Prototype]], although it reads backwards compared to FRU in ML-family languages. Those languages put the update on the right, often linked using 'with' (hah!).

It can be viewed that way, although I doubt that the vast majority of current JS programmers would recognize it as such...but that could change over time.

In general, I favor putting what is typically the longer operand second. Otherwise, the operator and right operand tend to get lost after a long left operand. In the case of functional record update, it appears that in most usage the update part is longer: e with {a=4, b=2} because the existing object is likely to be referenced via an identifier binding or function call and the update values are more likely to be some sort of longer literal.

The most common case for proto probably is the opposite as the "record" that is being modified is the operand that is more likely to be a long literal form: return myProto proto { a: 1, b:2, m1: function () (}, m2: function () {} };

Personally, I think the above makes it more obvious that we are returning an object with an explicitly specified prototype than the following: return { a: 1, b:2, m1: function () (}, m2: function () {} } proto myProto;

However, I acknowledge that in the above the object literal is probably the more important part of the expression and that having it first emphasizes that part.

It may be a toss up, I need to think about whether there are any other constructors in ES with which we should be striving for consistency. Perhaps, function() {}.bind(e) is one.

As you note in the strawman, this enables proto-presetting for a number of built-in classes, not just Object and Array.

I'm warming up, modulo the backward sense of the connective (also, you need a [no LineTerminator here] to the left of ''proto'' in the productions). Thinking outside the box, indeed.

yep, we want that to be a contextual operator keyword so we need to be careful about ASI.

Dmitry's suggestion of #proto or a similar sigil-distinguished name makes me wonder, though: could we have FRU via the spread operator and proto presetting without adding a funky [no LineTerminator here] proto infix operator?

var original_o = ...; var o = {#proto: p, ...original_o};

Given this, if you don't have an original_o to specialize (say, a RegExp or other built-in that can't be created via an initialiser), then of course

var o = {#proto: p, key: val, ...};

or var f= {#proto: p, ...function () {}}; var r= {#proto: p, .../[a-z]/}; var a={#proto: p, ...[1,2,3,4]); ???

For the cases of setting the prototype of a literal function/regexp/array I really prefer forcing the literal into immediate proximity of the prototype. eg, var f = p proto function () {}; or even var f = {#proto: p, ...function() {}} is much better than: var f0 = function() {}; //arbitrary lines of code var f = {#proto, ...f0};

If nothing else that latter is going to require significantly more analysis in the front-end to avoid creating two function objects.

Using spread in this way requires generalizing its semantics to copy not just properties but also magic internal properties.

Finally, I really don't want to have to use the {#proto: p, ...[1,2,3,4]} or var a0 =[1,2,3]; var a = {#proto: p, ...a0}; forms for arrays.

[#proto: p, 1,2,3] might be ok, but at the TC39 meetings there were concerns expressed about comma counting.

should be supported.

So, infix operators or magic property names?

For classes, at any rate, it seems to me that On the Outside and Declarative may be at odds, since initialisers are expressions. More below on why.

I'm kinda moving away from thinking that object literal and class declaration forms need to use common syntax.

The strongest case for extending initialiser syntax is that it is "nearly declarative" today. That suggests strongly making the extension fully declarative, i.e., not usable as an expression. And that, all else equal (never is, but:) says to consider class D extends B {...}. My 2 cents.

I don't think "declarative" should not be equated with "not usable as an expression " or introduces a lexical binding.

It's not clear that classes need to bind their names, or even need names. I believe that some on TC39 want non-generative, nominal run-time types via classes. Others may want generative expressions.

The consistent #1 request from users that I've heard over the years, most recently in my blog comments, has been for a declarative form of JS's prototypal object-oriented pattern. This conventionally means a new binding. It's generative if you nest it in an outer function, which is not a problem, but usually it is not nested, or else nested only in an outer IIFE (aka the module pattern).

that's the request I'm most interesting in satisfy with a "class" declaration. In that scenario "class" is close to meaning "constructor function" or just "function". That is why in my proposal class Foo {}; is defined to mean almost exactly the same thing as function Foo() {}; (but things start to change when you put something between the brackets) and anonymous classes exist as an analog to function expressions.

Both function declarations and function expressions are both declarative definitions of functions

Here is a bit of a nit, but calling both declarative even though one is an expression, not a declaration, sounds like a contradiction, or a redefinition of "declarat{ive,ion}".

I'm using "declarative" more (but not exactly) in this sense: "In computer science, declarative programming is a programming paradigm that expresses the logic of a computation without describing its control flow.[1] Many languages applying this style attempt to minimize or eliminate side effects by describing what the program should accomplish, rather than describing how to go about accomplishing it.[2] This is in contrast with imperative programming, which requires an explicitly provided algorithm." en.wikipedia.org/wiki/Declarative_programming

I would say that: ({a,x,b:y,c:z}) is declarative while {let obj = new Object; obj.a=x; obj.b=y; obj.c=z; obj} is not. It's imperative. They are both generative in that they create new objects whose initial state can only be determine at the time they are evaluated.

My usage has nothing to do with binding forms but my terminology seems to be causing confusing with binding forms in this context. I don't immediately have a another term at hand for a non-imperative, non-binding construction expression.

More substantive: if we want to enable optimizations and conveniences via syntax for the prototypal pattern, then declarations win, especially if they come with some ahead-of-time semantics, as top-level function declarations evaluated in a compile-and-go scenario do. Thanks to the order of function and then var declaration processing when entering an execution context, such a scenario supports the compiler pre-binding top-level functions.

I'm trying to relate this statement to the original "strongest case" comment above and I'm having a hard time. I probably something more concrete would help.

You're right that expressed as well as declared (whether top-level or not) forms could be supported. The question for classes is whether we would ever want an anonymous or named class expression. Just because 'function' can do that does not mean that 'class' must.

A key thing about the proto operator proposal (and different from my earlier meta properties proposals) is that it is trying to specifically address only the use case of providing an explicit [[Prototype]] for objects created using literal construction forms (object literals, array literals, regexp literals, function expressions). It does not currently address classes at all. If both this proto operator proposal and a class syntax gets adopted we would certainly want to revisit whether there can/should to be an commonality between the two. It's too early to tell.

# Dmitry A. Soshnikov (14 years ago)

On 28.03.2011 0:05, Brendan Eich wrote:

On Mar 27, 2011, at 11:33 AM, Juan Ignacio Dopazo wrote:

On Sat, Mar 26, 2011 at 6:31 PM, Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com <mailto:dmitry.soshnikov at gmail.com>> wrote:

Why not just to use already reserved `extends` keyword for
specifying a superclass? These XML-like braces looks not so elegant.

I asked this question a couple of days ago. The answer is quite simple. Object initializer extensions are more than just constructor syntax. They allow you to create complex objects without all the hassle in (function(){}()). So the constructor syntax was made this way to be consistent with object initializer syntax.

The meta syntax (e.g., var obj = {<proto: p>, key: val}) is not inconsistent with initialisers, it's an extension to them. But it's not the only self-consistent extension. Dmitry mentioned letting the meta properties appear to be regular key: value pairs but distinguishing the keys with @ or #.

Allen makes the point that class D extends B {...} may look too much like languages where it means something quite different. That's true, but I am not sure JS should be so special that it seems to mix bits of those languages into one (of several conceivable) extensions to initialiser syntax:

class D {<superclass B>};

Whatever you think of the meta section, the leading 'class D {' telegraphs influence from, and just familiarity with, those other languages. And I don't think that is a bad thing even if semantics differ.

Just changing 'class' to 'constructor' (adding a new perhaps-only-contextually reserved word, and a long one at that) is not the issue. Sure, doing that would in a text-only way make the syntax not look like any other language, exactly. But ignore the skin: the bones look familiar, going back to C structs.

Now add some notion of inheritance and you're following the C++ "body plan".

Exactly. "Classes" are not about just the "class" keyword, but about the ability to classify, i.e. to program in classified (i.e. with object-patterned programming). JS supports (and supported all these years both approaches: delegation-based prototypal (i.e. unclassified or chaotic code reuse) and classical (classified or systematized code reuse).

Thus, a "class" in JS is a pair of "constructor function + prototype". From this viewpoint, e.g. classes in Python (or CoffeeScript) are just syntactic sugar of the same delegation-based inheritance used in JS. And this means that JS also may have this sugar -- to allow users to program in classified manner (if they need to). So there's no a big difference between these languages and therefore keyword extends may fit absolutely normally.

Here how I see classes syntactic in JS (a combination of Coffee's sugar

  • Java's syntax, since we can't eliminate it).

Notes:

I think that usage of thing like: class method fromString() { .. } are to verbose. Don't think we need another keyword method at all.

Also I used a syntactic sugar for this keyword as @. This at allows easily to define and distinguish class- and instance- methods. The rule is simple: if a property/method inside the class body starts with @ -- it's the class's property/method (i.e. this refers to class). In other case -- it's an instance method. @ evaluated inside the instance method refers of course to the instance.

Here's a small example with sugared (on the left hand) and desugared (on the right hand side) views: bit.ly/hvS7hl Take a look please.

And below is just an example without the desugaring. Thus, the definiton itself is in declarative form. Also I think it's good to provide both dynamic and static classes. By default I used dynamic classes, so static keyword is explicit.

In the example I also used include keyword as an ability to mixin some module as a direct ancestor of an instance, i.e. before the class in the hierarchy. Mixins/traits will allow to reuse the code horizontally (not only with a single vertical inheritance "tower").

/* ------------------------------------------------------

  • Class Point definition

*/

class Point {

 /**
  * Class method (placed on constructor function)
  */
 @fromString: function (s) {
     return new Point(parse(s));
 }

 /**
  * Instance intializer (aka "constructor")
  */
 initialize: function (x, y) {
     @move(x, y);
 }

 /**
  * Simple instance method (placed on prototype)
  */
 move: function (x, y) {
     @x = x;
     @y = y;
     @repaint();
 }

 /**
  * Class property (placed on constructor function)
  */
 @x: 10

}

/* ------------------------------------------------------

  • Class Point3D definition

*/

static class Point3D extends Point {

 include Comparable;

 /**
  * Class static property
  */
 @instances: []

 /**
  * Instance intializer (aka "constructor")
  */
 initialize: function (x, y, z) {
     super(x, y);
     @z = z;
     @class.instances.push(@); // save the instance
 }

}

/* ------------------------------------------------------

  • Examples:

*/

// create new 3D point let point = new 3DPoint(10, 20, 30);

console.log(point); // "[instance 0x1382 of Point3D class]"

// inherited method

point.move(100, 200);

// introspect the class

console.log(point.class); // "[class Point3D]" console.log(point.class === Point3D); // true

// instrospect the super class

console.log(point.class.super); // "[class Point]" console.log(point.class.super === Point); // true console.log(Point3D.super === Point); // true

console.log(point instanceof Point3D); // true console.log(point instanceof Point); // true

// class method

let p = Point.fromString("10-20"); console.log(p.class); // "[class Point]"

// analyze class methods and properties console.log(Point.classMethods); // ["fromString"] console.log(Point.classProperties); // ["x"] console.log(Point.instanceMethods); // ["move"]

console.log(Point3D.classProperties); // ["instances"]

How does it look like? Do we need such a sugar? For both -- classes and this as @? Then # can be used for meta-properties on initialisers.

Dmitry.

# Allen Wirfs-Brock (14 years ago)

On Mar 28, 2011, at 1:53 PM, Dmitry A. Soshnikov wrote:

Exactly. "Classes" are not about just the "class" keyword, but about the ability to classify, i.e. to program in classified (i.e. with object-patterned programming). JS supports (and supported all these years both approaches: delegation-based prototypal (i.e. unclassified or chaotic code reuse) and classical (classified or systematized code reuse).

To quote the title of a famous paper by William Cook: "Inheritance Is Not Subtyping". This is commonly misquoted as "Subclassing is not Subtyping".

Thus, a "class" in JS is a pair of "constructor function + prototype". From this viewpoint, e.g. classes in Python (or CoffeeScript) are just syntactic sugar of the same delegation-based inheritance used in JS. And this means that JS also may have this sugar -- to allow users to program in classified manner (if they need to). So there's no a big difference between these languages and therefore keyword extends may fit absolutely normally.

And quoting the definition of class in Smalltalk-80: The Langauge and Its Implementation: "class: A description of a group of similar objects". I would emphasize the word "description" in the Smalltalk-80 definition. In dynamic object oriented languages, a class consists of the description of the common implementation of a set of identically implemented objects. Part of that common implementation may be obtained via inheritance. But obtaining parts of an object's implementation via inheritance (subclassing) is not the same as subtyping.

Subtyping implies a substitutive relationship. If B is a subtype of A, then in general you can expect to be able to substitute B in any situation where A is expected. Static object oriented languages in the style of C++, Java, and C# fairly strongly equate subclassing with subtyping. What you can say in a class definition is constrained by the rules of subtyping. Dynamic object-oriented languages such as Smalltalk, Ruby, and Python do not equate subclassing and subtyping. In such languages, B being a subclass of A means that B shares some of it implementation with A but it does not mean that B can always be substituted for A

One of the rules of object subtyping is that additional methods may be added by a subtype but methods may not be deleted. Thus in a subclassing==subtyping language it is easy to think about subclasses as generally "extending" superclasses with additional members. The use of "extends" in Java is no doubt a reflection of that perspective.

It is fairly straight forwards to augment JavaScript with the concept of "class" as a syntactic unit that describes the shared description of the implementation shared by a group of objects. It would also be straight forward to including the concept of implementation "subclassing" in the Smalltalk/Ruby/Python style. It would be very difficult to introduce the concept of classes as nominal types with subclassing into the JavaScript.

Using "extends" to mean "subclass of" may be familiar to Java programers, but it may also be misleading.

Here how I see classes syntactic in JS (a combination of Coffee's sugar + Java's syntax, since we can't eliminate it).

Notes:

I think that usage of thing like: class method fromString() { .. } are to verbose. Don't think we need another keyword method at all.

Also I used a syntactic sugar for this keyword as @. This at allows easily to define and distinguish class- and instance- methods. The rule is simple: if a property/method inside the class body starts with @ -- it's the class's property/method (i.e. this refers to class). In other case -- it's an instance method. @ evaluated inside the instance method refers of course to the instance.

this second usage of replacing this with @ seems like a separable piece and the same familiarity argument that might be made for using "extends" would argue against replacing "this" with "@".

# Brendan Eich (14 years ago)

On Mar 28, 2011, at 12:29 PM, Allen Wirfs-Brock wrote:

Dmitry's suggestion of #proto or a similar sigil-distinguished name makes me wonder, though: could we have FRU via the spread operator and proto presetting without adding a funky [no LineTerminator here] proto infix operator?

var original_o = ...; var o = {#proto: p, ...original_o};

Given this, if you don't have an original_o to specialize (say, a RegExp or other built-in that can't be created via an initialiser), then of course

var o = {#proto: p, key: val, ...};

or var f= {#proto: p, ...function () {}}; var r= {#proto: p, .../[a-z]/}; var a={#proto: p, ...[1,2,3,4]); ???

I was not proposing only the last form that I wrote in the nested citation above: var o = {#proto: p, key: val, ...}.

My point was simply that the single-initialiser-with-some-proto form will be wanted as the basis case (no record to update) of an inductive case where there is a pre-existing record (object) to update with a specific proto.

In any event, spreading a function or regexp should not do anything magic with its internal properties. Using spread for FRU handles only enumerable own properties, reading a bit between the lines of the strawman:records strawman.

Sorry, I didn't mean to drag records in here, just note how an ad-hoc proto operator seems like a specialized FRU if you buy Dmitry's #proto: p alterna-syntax.

That is why in my proposal class Foo {}; is defined to mean almost exactly the same thing as function Foo() {}; (but things start to change when you put something between the brackets) and anonymous classes exist as an analog to function expressions.

Is the non-empty braced body an initialiser (extended) or a constructor code body? I'm on record against the initialiser extension for precisely this reason: function has a code body.

More substantive: if we want to enable optimizations and conveniences via syntax for the prototypal pattern, then declarations win, especially if they come with some ahead-of-time semantics, as top-level function declarations evaluated in a compile-and-go scenario do. Thanks to the order of function and then var declaration processing when entering an execution context, such a scenario supports the compiler pre-binding top-level functions.

I'm trying to relate this statement to the original "strongest case" comment above and I'm having a hard time. I probably something more concrete would help.

If class C {...} is analogous to function in that each evaluation generates a new C and binds it to that name, where ... is constructor code, then the parallel to function is clear and implementations can optimize accordingly (as some already do for top level functions).

If {...} is an expression (initialiser, with extensions), then maybe the same optimizations apply at top level, but I'm not sure, and it doesn't look analogous to function any longer.

That's all. Not trying to make a mountain out of this, but either class C {...} is function-y or it isn't. Half-way in between, with an initialiser body, seems less good.

# Allen Wirfs-Brock (14 years ago)

On Mar 28, 2011, at 5:36 PM, Brendan Eich wrote:

On Mar 28, 2011, at 12:29 PM, Allen Wirfs-Brock wrote:

That is why in my proposal class Foo {}; is defined to mean almost exactly the same thing as function Foo() {}; (but things start to change when you put something between the brackets) and anonymous classes exist as an analog to function expressions.

Is the non-empty braced body an initialiser (extended) or a constructor code body? I'm on record against the initialiser extension for precisely this reason: function has a code body.

In my current proposal it's closer to an initialiser than a function body although it really isn't either. We really want a class body to be mostly declarative (that the compiler can optimize) rather than using imperative statements to construct the instances.

But we may be thinking about different kinds of optimizations. I'm thinking of predetermining the "shape" of the instances.

More substantive: if we want to enable optimizations and conveniences via syntax for the prototypal pattern, then declarations win, especially if they come with some ahead-of-time semantics, as top-level function declarations evaluated in a compile-and-go scenario do. Thanks to the order of function and then var declaration processing when entering an execution context, such a scenario supports the compiler pre-binding top-level functions.

I'm trying to relate this statement to the original "strongest case" comment above and I'm having a hard time. I probably something more concrete would help.

If class C {...} is analogous to function in that each evaluation generates a new C and binds it to that name, where ... is constructor code, then the parallel to function is clear and implementations can optimize accordingly (as some already do for top level functions).

In my current proposal class Foo {new (x) { {this.x=x} //imperative property creation like in a conventional constructor functions }} is analogous to function Foo(x) {this.x=x} but a better expression of the same thing would be: class Foo { new (x) { x: x //declarative property definition like in an object initialiser } }

You really want the declarative property form to be used as much as possible but sometimes you need to throw in a little bit of imperative initialization code.

You should probably take another look at strawman:obj_initialiser_class_abstraction as it cover all the variations.

If {...} is an expression (initialiser, with extensions), then maybe the same optimizations apply at top level, but I'm not sure, and it doesn't look analogous to function any longer.

I'm not sure which specific optimizations you have in mind. The initialiser form seems easier to shape analysis.

That's all. Not trying to make a mountain out of this, but either class C {...} is function-y or it isn't. Half-way in between, with an initialiser body, seems less good.

I guess, what I was trying to emphasize is that class C { } in my proposal really does create a constructor function and a prototype object just like a ES1 function declaration. Foo can be used in exactly the same way whether it is defined as class Foo ... or function Foo ... (eg, new Foo; Foo.prototype; Foo.prototype.constructor===Foo; etc.). However the body of a class needs to be more complex because it defines the properties of three+ objects: the constructor, the prototype, and all instances. It can't be just a function body and it can't just be just an initailiser body although it might be closer to one or another or something else completely different.

The main point of a class is to compartmentalize into a single unit the structural definition of the constructor, prototype, and instances. If all we wanted to define was the relationship between a constructor and a prototype, function already does that. We are really looking for a syntax to make:

function Foo(x) {this.inst=x}; //often: Foo.prototype = Object.create(superclass); Foo.prototype.m1 = function () {...}; Foo.prototype.m2 = function () {...}; Foo.cm1 = function() {...}

in to a single declarative construct where the constructor/prototype/instances methods and "instance variables" are easily identifiable by humans and tools.

# Dmitry A. Soshnikov (14 years ago)

On 29.03.2011 2:51, Allen Wirfs-Brock wrote:

On Mar 28, 2011, at 1:53 PM, Dmitry A. Soshnikov wrote:

Exactly. "Classes" are not about just the "class" keyword, but about the ability to classify, i.e. to program in classified (i.e. with object-patterned programming). JS supports (and supported all these years both approaches: delegation-based prototypal (i.e. unclassified or chaotic code reuse) and classical (classified or systematized code reuse).

To quote the title of a famous paper by William Cook: "Inheritance Is Not Subtyping". This is commonly misquoted as "Subclassing is not Subtyping".

Thus, a "class" in JS is a pair of "constructor function + prototype". From this viewpoint, e.g. classes in Python (or CoffeeScript) are just syntactic sugar of the same delegation-based inheritance used in JS. And this means that JS also may have this sugar -- to allow users to program in classified manner (if they need to). So there's no a big difference between these languages and therefore keyword extends may fit absolutely normally.

And quoting the definition of class in /Smalltalk-80: The Langauge and Its Implementation/: "class: A description of a group of similar objects". I would emphasize the word "description" in the Smalltalk-80 definition. In dynamic object oriented languages, a class consists of the description of the common implementation of a set of identically implemented objects. Part of that common implementation may be obtained via inheritance. But obtaining parts of an object's implementation via inheritance (subclassing) is not the same as subtyping.

Subtyping implies a substitutive relationship. If B is a subtype of A, then in general you can expect to be able to substitute B in any situation where A is expected. Static object oriented languages in the style of C++, Java, and C# fairly strongly equate subclassing with subtyping. What you can say in a class definition is constrained by the rules of subtyping. Dynamic object-oriented languages such as Smalltalk, Ruby, and Python do not equate subclassing and subtyping. In such languages, B being a subclass of A means that B shares some of it implementation with A but it does not mean that B can always be substituted for A

Allen, all this is correct, though I guess we understand that we talk about not subtyping as substitution principle (by Liskov), but about the sugar for generation of objects with the same structure -- to improve code reuse.

Regarding classes in general I have the following table of "classes" kinds:

     |    first-class                 |   second-class (or 

"first-order") --------|-----------------------------

# Dmitry A. Soshnikov (14 years ago)

Besides. I'd like to propose also :: syntax (can it break LL handwritten parsers?) to improve monkey-patching of the dynamic classes. It uses in C++ and also in Coffee as a sugar for "prototype" property.

Point::create = function () { ... };

desugars into:

Point.prototype.create = function () { ... };

Though, I don't see how to define a property on the "class's prototype". Only with involving "prototype" property:

Object.defineProperty(Point.prototype, "create", { value: function () { ... }, enumerable: false });

Or. To provide the Class meta-class which will be able to create classes at runtime (as expressions) and additionally:

Class.defineInstanceProperty(Point, "create", { ... })

which desugars again into:

Object.defineProperty(Point.prototype, "create", { ... })

and probably Class.defineStaticProperty(Point, "instances", { ... })

which maybe is overhead as can be achieved with Object.defineProperty(Point, "instances", { ... });

However, I'm not sure about Class.create(...) since it backs us to a simple wrapper-function used in many libraries providing programming with classes. Though, class expression can be useful:

let Foo = isDebug ? class Foo { ... } : class Foo { ... }

P.S.:

Let myself to repeat it again, whatever syntax will be chosen (whether with extends or not), I just want it to be really a sugar. In other case there will be no any sense because every library can provide classes sugar itself. And these "class method", "class method", "class method" aren't sugar at all. I consider <meta properties ... > also as

not so sugared and still thing that # or @ (if not for this) are better (this includes Brendan's proposal with the spread).

Dmitry.

# Juan Ignacio Dopazo (14 years ago)

Mixins are a great idea. Whatever decision is made about the position and name of the inheritance declaration, they add a lot of value and are very easy to understand.

A couple of related ideas:

class Vector { <superclass: Point, uses: Comparable> }

"uses" looks like a better fit. It's shorter and it's already used by many documentation generators (YUI Doc comes to mind). Also, more than one mixin could be easily added:

class Vector { <superclass: Point, uses: [Comparable, Measurable]> }

Juan

# Allen Wirfs-Brock (14 years ago)

On Mar 29, 2011, at 12:14 AM, Dmitry A. Soshnikov wrote:

On 29.03.2011 2:51, Allen Wirfs-Brock wrote:

...

Regarding classes in general I have the following table of "classes" kinds:

    |    first-class                 |   second-class (or "first-order")

--------|------------------------------- |----------------------------------- dynamic | Ruby, Python, JS, Coffee | ? static | freeze(class) | C++, Java

Thus, combination of "statics + second-class" can give us an immutable type with strong predefined behavior and set of properties.

FWIW, according to this model, Smalltalk has a static, first-class classes and I believe most of your points below also apply to such languages

In dynamic classes first-class classes, a "type" as a set of predefined and immutable things is not so important. Moreover, for feature-testing, as a "type-tag" or better to say as a "classification-tag" can be used just a simple property of an object which helps to distinguish an object of yours classification from the object with the same state.

foo.class == Foo; // true -- testing with a class-tag foo instanceof Foo; // true

It's enough for dynamic first-class classes, and substitution principle may not be so important. Moreover, even here, e.g. the following substitution works fine:

bar instanceof Bar; // true bar instanceof Foo; // true, assuming that Bar is a subclass of Foo

And the set of methods and properties in the dynamic classes of course can vary over the time. And of course in such a system we cannot predict whether will be able to substitute an instance after some mutations (removing/addition methods, etc). But repeat, it's not so required hard in the dynamic system. But if you still want be sure, the just make them completely frozen (i.e. static classes) and then you can be sure.

There is much Smalltalk experience that shows that "class testing" leads to code that is less maintainable or less reusable. Smalltalk style guides (for example see stephane.ducasse.free.fr/FreeBooks/WithStyle/SmalltalkWithStyle.pdf ) invariably contained items like "don't test for a specific class" and "don't use isKindOf: or isMemberOf:" (tests for inherited/direct class membership). The reason is that you should care more about the behavioral contract of an object rather than which specific implementation class it is an instance of as there may be multiple classes whose instances are behaviorally interchangeable from some specific perspective. Behavior tests such as classification properties of the sort you mention are fine. Implementation class tests such as instanceof are not, even though as a practical matter they need to be available.

So what is more important here (and exactly about it is your strawman as I understand, right?) is the syntactic sugar for exactly convenient classified programming. For the convinient classified generation of objects created by the specified (classified) patterns. And exactly from the convenience of the usage of such a sugar I think we should start.

One of the things I was trying to say was that the most important thing about syntactic class definitions is their "modularity". They group all the elements that define the a set of objects with a common implementation into a single modular unit. The class declaration is the "module" that provides this grouping. The fact that you can use class membership for classification purposes is secondary and as mentioned above often undesirable.

One of the rules of object subtyping is that additional methods may be added by a subtype but methods may not be deleted. Thus in a subclassing==subtyping language it is easy to think about subclasses as generally "extending" superclasses with additional members. The use of "extends" in Java is no doubt a reflection of that perspective.

Yes, that's true, but it seems a little bit as a nit-picking to exact syntax/terminology. Instead, we should concentrate on exactly the convenient code reuse and convenient classified generation. Repeat, it doesn't matter much in dynamic system whether we'll remove some method (and therefore we shouldn't use keyword "extends"). If you just don't like exactly this keyword (assuming statics and only extension, not modification of descendant classes) then we may use any other word which you think fits better. E.g. inherits -- class B inherits A. Or symbols -- yours, proposed on Twitter (C++'s actually) colon : -- class B : A. Or Ruby's one -- class B < A (which is also logical -- "the class B is less than A").

But I think exact keyword isn't so important in this case. extends keyword is just familiar -- yes, from Java's syntax (actually JavaScript uses Java's syntax). And it doesn't matter and a Java programmer doesn't know that a class in JS can be dynamic and that after the extension there can be other modifications.

I agree with you that the specific syntax for specifying the "superclass" is secondary and it wouldn't be the end of the word if we used the keyword "extends". But I did want point out that its familiarity may carry additional baggage. I also think there is some tension in this community between those who would prefer a "second-class static" subtyping based class model and those who are quite content with a "first class dynamic or static" inheritance based class model. If we are going to provide a class syntax we need to balance those perspectives or at least understand which we are favoring in our design decisions.

...

What I want is to provide more convenient programming. What exactly you don't like in @ as this? Isn't it convenient to describe class methods as just " (1) this evaluated in the class body -- is the class. (2) this evaluated inside instance method -- is an instance". And do you think the sugar I described is a good declarative form of classes sugar (or if not -- what did I do wrong?) ?

I think we already have "this" and it is too late to replace it with "@" in expressions.

Writing every time "class method", "class method", "class method" not only too long (and therefore boring), but also will highlight this class keyword many times (which will annoy the users).

I think we can probably get rid of the "method". "class" doesn't bother me as being an excessively heavy weight of designate the relatively rare occurrences of constructor properties. Over all, I think this community tends to over use the need to minimize typing argument. Most lines of code are read many more times than they are written so there is a good case to be made for preferring readability or writability. Constructs that are used relatively infrequently should be more biased towards supporting readability while writability matters more for very frequently used constructs.

...

# Dmitry A. Soshnikov (14 years ago)
# Juan Ignacio Dopazo (14 years ago)

I've been thinking about something Brendan said in his last podcast about how the community and TC39 can't agree on the syntax for class initializers because of how flexible he made it at the beginning. So a little idea came to me.

How about adding an Object.getMetaProperties or Function.getMetaProperties method to retrieve the meta properties of an object/class initializer, regardless of where they were declared (inside or outside)? That way the community could extend the syntax to fit different styles. Combined with proxies it could lead to many different multiple inheritance or mixin patterns, as well as other interesting applications I'm probably missing.

Of course, this would be extra useful if meta properties where defined outside the class/object body, because there's not much difference between writing

class A { <superclass: B, uses: [C]> }

or

class A { <superclass: B>, class var USES: [C] }

...as we do today using constructor properties to add extra functionality.

Being able to write somethink like...

class A uses [C, D] { new () { let extensions = Function.getMetaProperty(A, 'uses') || []; extensions.forEach(#(ext) { // Copy ext prototype methods to A }); } }

...could be useful and fun.

Juan