Alternative syntax for <|

# Russell Leggett (12 years ago)

Given the recent conversation about class as operator and its likely composition with <|, I propose turning the syntax:

*MemberExpression* <| *ProtoLiteral*

into

extends *MemberExpression ProtoLiteral*

In the common case of using the class operator, it would read much more naturally:

let Point2d = class extends Point {
    ...
}

or how I propose it, allow using a name, and define it:

class Point2d extends Point {
    ...
}
//which can be grouped as
class Point2d (extends Point {
    ...
})

Non-class uses of the syntax might seem a little bit odd

let specialArray = appBehavior <| [0,1,2,3,4,5]
//becomes
let specialArray = extends appBehavior [0,1,2,3,4,5]

but given the overall disapproval of the exact syntax of the <| operator, I think its at least a little more obvious what is going on.

# Dmitry Soshnikov (12 years ago)

On 16.11.2011 10:27, Russell Leggett wrote:

Given the recent conversation about class as operator and its likely composition with <|, I propose turning the syntax: /MemberExpression/ <| /ProtoLiteral/ into

extends /MemberExpression ProtoLiteral/

In the common case of using the class operator, it would read much more naturally:

let Point2d = class extends Point {
    ...
}

or how I propose it, allow using a name, and define it:

class Point2d extends Point {
    ...
}
//which can be grouped as
class Point2d (extends Point {
    ...
})

Non-class uses of the syntax might seem a little bit odd

let specialArray = appBehavior <| [0,1,2,3,4,5]
//becomes
let specialArray = extends appBehavior [0,1,2,3,4,5]

Yes, it was mentioned before (especially taking into account that `extends' is already reserved).

However, there were arguments about wrong placement of entities regarding to `extends' (it turned out that it's like a prototype extends an object, but it should be vice-versa).

You put it in the correct place, though as I see we may even eliminate assignment here `=' and to make it even more declarative:

let point = {x: 10, y: 20};

// a simple object let point3D extends point { z: 30 }

// a class class Point3D extends Point { constructor (x, y, z) { ... } }

Since such declarations usually appear exactly in declarative form (sorry for tautology), assignment can be omitted.

How is it different from Object.create(...)? Actually yes, it's just an alternative and in contrast with O.create, I think here we may use not descriptors but directly values.

P.S.: in addition, will we introduce another type-tag testing? Or instanceof' and testing constructor property will be enough? I would agree and recommendclass' operator as well -- to return, how proposed by Allen, the value of the constructor', actually just a shorthand for not to test theconstructor':

if (class(foo) == Foo) // like Python's type

P.S.[2]: and regarding assignment, I think it can be used in class-expressions (notice also, unfortunately only hypothetical, since the topic became silent, if-expression)

let OperationClass = if (in3dMode) { class extends Point3D { // implementation } } else { class extends Point { // ... } }

Dmitry.

# Russell Leggett (12 years ago)

On Wed, Nov 16, 2011 at 4:42 AM, Dmitry Soshnikov < dmitry.soshnikov at gmail.com> wrote:

On 16.11.2011 10:27, Russell Leggett wrote:

Given the recent conversation about class as operator and its likely composition with <|, I propose turning the syntax:

*MemberExpression* <| *ProtoLiteral*

into

 extends *MemberExpression ProtoLiteral*

In the common case of using the class operator, it would read much more naturally:

 let Point2d = class extends Point {
    ...
}

or how I propose it, allow using a name, and define it:

 class Point2d extends Point {
    ...
}
//which can be grouped as
class Point2d (extends Point {
    ...
})

Non-class uses of the syntax might seem a little bit odd

 let specialArray = appBehavior <| [0,1,2,3,4,5]
//becomes
let specialArray = extends appBehavior [0,1,2,3,4,5]

Yes, it was mentioned before (especially taking into account that `extends' is already reserved).

Yes, that's why I was hoping to consolidate - I know extends is what many want to use with classes, thus why it is reserved.

However, there were arguments about wrong placement of entities regarding to `extends' (it turned out that it's like a prototype extends an object, but it should be vice-versa).

You put it in the correct place, though as I see we may even eliminate assignment here `=' and to make it even more declarative:

let point = {x: 10, y: 20};

// a simple object let point3D extends point { z: 30 }

// a class class Point3D extends Point { constructor (x, y, z) { ... } }

Since such declarations usually appear exactly in declarative form (sorry for tautology), assignment can be omitted.

Yes, I would love to see that in the sense that I dislike the alternative. let Point2d = Point <| {...}, to me, just puts the name too separate from the implementation. That is why I'm advocating for the class operator to have a bit of the function declaration flavor. However, I wonder if it starts to become too much additional complication for the language to have these special forms of let/const. When I see a let, I expect an assignment.

How is it different from Object.create(...)? Actually yes, it's just an alternative and in contrast with O.create, I think here we may use not descriptors but directly values.

P.S.: in addition, will we introduce another type-tag testing? Or instanceof' and testing constructor property will be enough? I would agree and recommendclass' operator as well -- to return, how proposed by Allen, the value of the constructor', actually just a shorthand for not to test theconstructor':

if (class(foo) == Foo) // like Python's type

P.S.[2]: and regarding assignment, I think it can be used in class-expressions (notice also, unfortunately only hypothetical, since the topic became silent, if-expression)

let OperationClass = if (in3dMode) { class extends Point3D { // implementation } } else { class extends Point { // ... } }

if class declarations take expressions as I'm proposing, and if and extends are just expressions, you could also do

class OperationClass if (in3dMode) {
  extends Point3D {
    // implementation
  }
} else {
  extends Point {
    // ...
  }
}
# Greg Smith (12 years ago)

"How is it different from Object.create(...)? Actually yes, it's just an alternative and in contrast with O.create, I think here we may use not descriptors but directly values."

The importance of this difference can't be understated. Object.create's verbosity makes it a poor idiom for everyday programming. You end up creating your own helper so you can provide your properties as a normal object literal eg {x:5,y:10} . If it invites you to create a helper, it's not good enough. This proposal is fully suited to everyday programming needs and that makes it perfect.

# Jake Verbaten (12 years ago)

I think there was a naming discussion in another thread.

Popular alternatives seemed to be "beget" and "proto"/"protos". I still seem to like "beget"

# Russell Leggett (12 years ago)

On Wed, Nov 16, 2011 at 10:31 AM, Jake Verbaten <raynos2 at gmail.com> wrote:

I think there was a naming discussion in another thread.

Popular alternatives seemed to be "beget" and "proto"/"protos". I still seem to like "beget"

The reason I suggest it is entirely based on its relation to the potential class operator. I see class operator combined with extends operator having a very natural feel (effectively makes Jeremy Ashkenas' class proposal happen), while still being able to be used orthogonally. The problem with beget or proto is that its still in the wrong place.

const Point2d = class Point beget {
    ...
}

//or anonymously

return class Point beget {...} //reads like you're creating a class

Point here

If class is off the table, then I would probably retract my proposal for extends. If class is on the table, but with leather and its own built-in extends keyword, I would probably still advocate for my form instead of <| because at least it would be consistent.

# Jake Verbaten (12 years ago)

It's simply that

var x = someProto beget { ... }

reads nicer then

var x = someProto extends { ... }

I'd prefer to have readability on the non class related operator then the class related operator

# Russell Leggett (12 years ago)

On Wed, Nov 16, 2011 at 11:07 AM, Jake Verbaten <raynos2 at gmail.com> wrote:

It's simply that

var x = someProto beget { ... }

reads nicer then

var x = someProto extends { ... }

I'd prefer to have readability on the non class related operator then the class related operator

I was actually suggesting extends on the left - so

var x = extends someProto { ... }

Which reads better than someProto extends { ... }, because that reads as though {...} is the parent.

# Brendan Eich (12 years ago)

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

On Wed, Nov 16, 2011 at 11:07 AM, Jake Verbaten <raynos2 at gmail.com> wrote: It's simply that

var x = someProto beget { ... }

reads nicer then

var x = someProto extends { ... }

I'd prefer to have readability on the non class related operator then the class related operator

I was actually suggesting extends on the left - so

var x = extends someProto { ... }

Which reads better than someProto extends { ... }, because that reads as though {...} is the parent.

I think you cracked an important problem here. Someone on my blog commented "why not infix extends" (my paraphrase) but with proto on the left that's backwards. Putting proto on the right of infix 'extends' does not work well because the object literal on the left can be large, making the extends and the (generally shorter) proto expression hard to see. More to your point, that also misaligns with

class C extends B {...}.

If we can get class as a unary operator right, or even if not due to <| looking bad in too many common fonts, then your prefix binary 'extends' operator looks winning to me. It's not perfect, but nothing is.

# Erik Arvidsson (12 years ago)

One thing that all of these discussions are missing is the hoisting property of function and any possible future classes. If we use "let Point = ..." we lose all hoisting and the order of your declarations starts to matter and we will end up in the C mess where forward references do not work.

# Dmitry Soshnikov (12 years ago)

On 16.11.2011 23:12, Erik Arvidsson wrote:

One thing that all of these discussions are missing is the hoisting property of function and any possible future classes. If we use "let Point = ..." we lose all hoisting and the order of your declarations starts to matter and we will end up in the C mess where forward references do not work.

No matter, we may rewrite it with `var':

// parent object var point = {x: 10, y: 20};

// child object var point3D extends point { z: 30 }

// a class class Point3D extends Point { constructor (x, y, z) { ... } }

It seems interesting for me, since we define both -- child classes and child objects with the same syntactic construction; only var' andclass' keywords change.

Dmitry.

# Brendan Eich (12 years ago)

On Nov 16, 2011, at 11:12 AM, Erik Arvidsson wrote:

One thing that all of these discussions are missing is the hoisting property of function and any possible future classes. If we use "let Point = ..." we lose all hoisting and the order of your declarations starts to matter and we will end up in the C mess where forward references do not work.

Allen suggested classes can't hoist here:

esdiscuss/2011-August/016212

Quoting:

Let me try a more carefully constructed example:

class First() { class: partner = (new Second).getPartner(); competitor = (new Second).getCompetitor(); } class Second() { constructor() { this.getCompetitor = function() {/whatever/*} } getPartner() {/whatever/}} }

which viewed using the obvious desugaring with hosting ould presumably be:

function First() {} function Second() {ihis.getCompetitor = function() {/whatever/*}} ... First.partner = (new Second).getPartner(); //but getPartner is not defined yet First. competitor = (new Second).getCompetitor(); //but getCompetitor is ok Second.prototype.getPartner = function() {/whatever/}}

Hosting of function works for resolving references from within function bodies. But individual property initialization expressions for a class are evaluated in order and can have order dependencies. Function declarations don't have any corresponding parts that expose such dependencies but classes do. Hosting does not eliminate those dependencies.

This may seem to rely on class ("static") properties, but we want to be future-proof even if classes don't support those at first.

# Quildreen Motta (12 years ago)

But with `var', only the declaration is hoisted, the actual assignment is not. Such that:

var point3d = extends point { z: 1 } var point = { x: 1, y : 1 }

Would still fail, as far as I know, whereas:

class point3d extends point { z: 1 } class point { x: 1, y: 1 }

Should work, if class is hoisted.

2011/11/16 Dmitry Soshnikov <dmitry.soshnikov at gmail.com>

# Dmitry Soshnikov (12 years ago)

On 16.11.2011 23:19, Quildreen Motta wrote:

But with `var', only the declaration is hoisted, the actual assignment is not. Such that:

var point3d = extends point { z: 1 } var point = { x: 1, y : 1 }

Right, but I used declarative form, w/o assignment:

var point3d extends point { z: 1}

OTOH, an expression (yours example w/ assignment) will be eval'ed in the runtime, yes.

P.S.: Kh-mm... but how the declarative form can eval the block on entering the context if the block contains some complex data? .. it means all the data should be defined already anyway.

E.g.:

var point3D extends point { z: calculate(); }

calculate should be defined and the execution is made in the runtime, i.e. again, just `var' is hoisted. But, it seems OK -- the same as with classes.

Dmitry.

# Andreas Rossberg (12 years ago)

On 16 November 2011 20:12, Erik Arvidsson <erik.arvidsson at gmail.com> wrote:

One thing that all of these discussions are missing is the hoisting property of function and any possible future classes. If we use "let Point = ..." we lose all hoisting and the order of your declarations starts to matter and we will end up in the C mess where forward references do not work.

The order of let declarations does not matter for visibility. It is perfectly fine to forward reference let, as long as it does not immediately require the value. For example:

let x = {f: function() { return y;}} let y = 5

In particular, this enables mutually recursive let definitions.

# Rick Waldron (12 years ago)

As stated previously[1], my support for "begets" as pure win is unwavering.

Rick

[1] esdiscuss/2011-October/017758

# Dmitry Soshnikov (12 years ago)

But why do we need new keyword?

Actually, I can accept a new keyword in case if reasons will really be serious. But in this case -- `extends' may fit quite nice.

P.S.: In general, it's just funny picture. Somehow ES4 made nice classes with nice syntax. Why (Why Y NO?) now we invent some scary things such as const Point = Point <| {}.constructor.{}.prototype{}. Just... why? What has happened with syntax idea even from ES4?

Dmitry.

# Erik Arvidsson (12 years ago)

On Wed, Nov 16, 2011 at 11:29, Andreas Rossberg <rossberg at google.com> wrote:

On 16 November 2011 20:12, Erik Arvidsson <erik.arvidsson at gmail.com> wrote:

One thing that all of these discussions are missing is the hoisting property of function and any possible future classes. If we use "let Point = ..." we lose all hoisting and the order of your declarations starts to matter and we will end up in the C mess where forward references do not work.

The order of let declarations does not matter for visibility. It is perfectly fine to forward reference let, as long as it does not immediately require the value. For example:

let x = {f: function() { return y;}} let y = 5

In particular, this enables mutually recursive let definitions.

Sorry for being too brief. Today the following works.

f(); ... function f() { ... }

but the following does not:

f(); ... let f = function f() {};

I think it is important that we keep the forward reference behavior with classes. This requires a declarative way of defining classes.

# Rick Waldron (12 years ago)

On Wed, Nov 16, 2011 at 2:34 PM, Dmitry Soshnikov < dmitry.soshnikov at gmail.com> wrote:

But why do we need new keyword?

The discussion originated around how ugly <| is; an infix operator keyword is easier to introduce into the language then an infix operator made of symbols

Actually, I can accept a new keyword in case if reasons will really be serious. But in this case -- `extends' may fit quite nice.

See: esdiscuss/2011-October/017775

# Andreas Rossberg (12 years ago)

On 16 November 2011 20:45, Erik Arvidsson <erik.arvidsson at gmail.com> wrote:

Sorry for being too brief. Today the following works.

f(); ... function f() { ... }

but the following does not:

f(); ... let f = function f() {};

I think it is important that we keep the forward reference behavior with classes. This requires a declarative way of defining classes.

Ah, yes, that is a good point!

# Dmitry Soshnikov (12 years ago)

On 16.11.2011 23:53, Rick Waldron wrote:

On Wed, Nov 16, 2011 at 2:34 PM, Dmitry Soshnikov <dmitry.soshnikov at gmail.com <mailto:dmitry.soshnikov at gmail.com>> wrote:

But why do we need new keyword?

The discussion originated around how ugly <| is; an infix operator keyword is easier to introduce into the language then an infix operator made of symbols

Yes, I understand, but it doesn't answer the question -- why do we need additional keyword if to represent e.g. like this:

// declaration

var foo extends bar { x: 100 }

// or via expression:

var foo = extends (debug ? bar { x: 100 } : baz { y : 200});

// or even var foo = extends (debug ? bar : baz) {x : 100}

Anyway, forms with assignment are additions. The main part which I notice is the declarative form. Once again, a good thing is that we have the same syntactic form for defining/inhering simple objects and classes (only var/let and class keywords change).

Dmitry.

Actually, I can accept a new keyword in case if reasons will
really be serious. But in this case -- `extends' may fit quite nice.

See: esdiscuss/2011-October/017775

I believe I saw this discussion; it doesn't explain also why we can't do as I showed above (i.e. the declarative form with correct placemend of `extends').

P.S.: In general, it's just funny picture. Somehow ES4 made nice
classes with nice syntax. Why (Why Y NO?) now we invent some scary
things such as const Point = Point <|
{}.constructor.{}.prototype{}. Just... why? What has happened with
syntax idea even from ES4?

By the way, the question is still open and I'll be glad to hear at least small historical note on it.

P.S.: on <| -- speaking the truth I don't think it's ugly. Well, perhaps maybe I use to Ruby's < inheritance op. Maybe it's just "pipe part" | of it is ugly? :P

Dmitry.

# Russell Leggett (12 years ago)

On Wed, Nov 16, 2011 at 3:11 PM, Dmitry Soshnikov < dmitry.soshnikov at gmail.com> wrote:

** On 16.11.2011 23:53, Rick Waldron wrote:

On Wed, Nov 16, 2011 at 2:34 PM, Dmitry Soshnikov < dmitry.soshnikov at gmail.com> wrote:

But why do we need new keyword?

The discussion originated around how ugly <| is; an infix operator keyword is easier to introduce into the language then an infix operator made of symbols

Yes, I understand, but it doesn't answer the question -- why do we need additional keyword if to represent e.g. like this:

// declaration

var foo extends bar { x: 100 }

// or via expression:

var foo = extends (debug ? bar { x: 100 } : baz { y : 200});

This would not work - extends should take one expression and one proto-literal

// or even var foo = extends (debug ? bar : baz) {x : 100}

However, this one should work.

Anyway, forms with assignment are additions. The main part which I notice is the declarative form. Once again, a good thing is that we have the same syntactic form for defining/inhering simple objects and classes (only var/let and class keywords change).

The problem I would have with 'var foo extends...' is that it is very specific to the extends keyword. I think it's pretty intuitive still, so I can't say I'm totally opposed - but it does seem like tightly coupling two things which are orthogonal.

In my proposal of the class operator, it would basically work like function, only the class body is allowed to be any expression resulting in an object.

//expression form
var Foo = class <Expression>;

//expression form with name
//Bar is the variable, but MyName is the name of the constructor

function var Bar = class MyName <Expression>;

//definition form, creates a hoisted variable Baz, and makes
//that the name of the constructor function
class Baz <Expression>

P.S.: on <| -- speaking the truth I don't think it's ugly. Well, perhaps maybe I use to Ruby's < inheritance op. Maybe it's just "pipe part" | of it is ugly? :P

I think its a new operator, and I haven't seen it anywhere else. Ruby's use of < is not as bad because it can only be used in a class definition, and therefore can be inferred a little better what's going on.

# David Herman (12 years ago)

On Nov 16, 2011, at 12:11 PM, Dmitry Soshnikov wrote:

Yes, I understand, but it doesn't answer the question -- why do we need additional keyword

Infix operators can be conditional keywords. That's the current plan for is, IINM.

# Brendan Eich (12 years ago)

On Nov 16, 2011, at 9:31 PM, David Herman wrote:

On Nov 16, 2011, at 12:11 PM, Dmitry Soshnikov wrote:

Yes, I understand, but it doesn't answer the question -- why do we need additional keyword

Infix operators can be conditional keywords. That's the current plan for is, IINM.

and isnt.

harmony:egal

# Greg Smith (12 years ago)

The biggest advantage to beget is that it has the opportunity to become The Prototype Word. "inherit" has a lot of people associating it with classical OO programming. As nice as inherit sounds, it might be good to drop that baggage.

# Jason Orendorff (12 years ago)

On Wed, Nov 16, 2011 at 1:12 PM, Erik Arvidsson <erik.arvidsson at gmail.com> wrote:

One thing that all of these discussions are missing is the hoisting property of function and any possible future classes. If we use "let Point = ..." we lose all hoisting and the order of your declarations starts to matter and we will end up in the C mess where forward references do not work.

Can you give sample code where this is really a problem?

I think it's a problem in C/C++ because of early binding and because the C/C++ parser has to recognize typenames.

In ES, the scope of "let Point = ..." is the enclosing block, right? Forward references should work fine.

# David Herman (12 years ago)

On Nov 17, 2011, at 8:00 AM, Jason Orendorff wrote:

On Wed, Nov 16, 2011 at 1:12 PM, Erik Arvidsson <erik.arvidsson at gmail.com> wrote:

One thing that all of these discussions are missing is the hoisting property of function and any possible future classes. If we use "let Point = ..." we lose all hoisting and the order of your declarations starts to matter and we will end up in the C mess where forward references do not work.

Can you give sample code where this is really a problem?

People take advantage of the fact that they can define their functions wherever they want in JS and they'll already be initialized. It's perfectly reasonable to have a bunch of classes and want to group them together thematically, and not have to sort them in order of initialization.

I think it's a problem in C/C++ because of early binding and because the C/C++ parser has to recognize typenames.

In ES, the scope of "let Point = ..." is the enclosing block, right? Forward references should work fine.

This isn't about scope, it's about at what point they're initialized. If you write:

let x = new C();
let C = class /* whatever */;

you won't get a scope error but a runtime initialization error. Whereas if you write:

let x = new C();
class C { ... }

it'll work fine.

I'm with Arv 150% on this.

# Jason Orendorff (12 years ago)

On Thu, Nov 17, 2011 at 10:40 AM, David Herman <dherman at mozilla.com> wrote:

This isn't about scope, it's about at what point they're initialized. If you write:

let x = new C();    let C = class /* whatever */;

you won't get a scope error but a runtime initialization error. Whereas if you write:

let x = new C();    class C { ... }

it'll work fine.

I'm with Arv 150% on this.

I'm with Allen. If ES classes can contain any initialization code, I think it should run in program order, interleaved with top-level statements. Anything else is just confusing.

Note that classdefs in Ruby and Python aren't hoisted, and nobody complains. In those languages classdefs very often contain procedural code, for many purposes.

class Card   # Ruby
    attr_accessor :rank, :suit    # macro-like
end

class File:  # Python
    size = property(get_size)    # create property descriptor
    if os.name == 'posix':    # ifdef-like
        ... posix-only methods ...
# David Flanagan (12 years ago)

On 11/16/11 11:45 AM, Erik Arvidsson wrote:

Sorry for being too brief. Today the following works.

f(); ... function f() { ... }

but the following does not:

f(); ... let f = function f() {};

I think it is important that we keep the forward reference behavior with classes. This requires a declarative way of defining classes.

I agree with Erik and Dave that forward references are critical for classes. If we define syntax for classes that is less foward-reference friendly than functions are, it will be a source of bugs (speaking from personal experience) and be perceived as a step backward.

A declarative syntax for defining classes is the goal of the classes proposal that has already been accepted into the harmony namespace harmony:classes

ECMAScript already has excellent features for defining abstractions for kinds of things. The trinity of constructor functions, prototypes, and instances are more than adequate for solving the problems that classes solve in other languages. The intent of this strawman is not to change those semantics. Instead, it's to provide a /terse/ and /declarative/ surface for those semantics so that /programmer intent/ is expressed instead of the /underlying imperative machinery/.

But back to the topic of forward references, note that with today's class pattern of defining a constructor function and then initializing its prototype properties, we can write forward references to the constructor, and we can often invoke the constructor through a forward reference, but we can't invoke any methods on the initialized instances until the prototype object is actually initialized. So forward references are only partially possible today. A declarative class syntax that initializes the prototype as part of the hoisting process would be nice, though of course forward references from a prototype to another class will be tricky.

Also, today, in the absence of modules, some programmers give up the ability to use forward references in order to define their classes in a private scope:

 let Class = (function() { function C() {...}; C.prototype = {...}; 

return C; }());

I'm not familiar enough with the ES.next modules proposal to know what impact it has on forward references...

# Brendan Eich (12 years ago)

On Nov 17, 2011, at 10:17 AM, Jason Orendorff wrote:

On Thu, Nov 17, 2011 at 10:40 AM, David Herman <dherman at mozilla.com> wrote:

This isn't about scope, it's about at what point they're initialized. If you write:

let x = new C(); let C = class /* whatever */;

you won't get a scope error but a runtime initialization error. Whereas if you write:

let x = new C(); class C { ... }

it'll work fine.

I'm with Arv 150% on this.

I'm with Allen. If ES classes can contain any initialization code,

That's a big "if" right now.

The only way I see this happening is via static data property initialisers. The definition of static properties (of any kind) is not observable on the class or its prototype if |this| is verboten in initialiser expressions (for |this| read any keyword that allows access to the class constructor or prototype).

Of course, initialiser expressions could have side-effects in the heap (increment a global counter, e.g.). That's considered bad practice and I'm not sure worrying about it trumps the classes-as-sugar argument for hoisting: function declarations hoist today, people write constructors this way (most of the time), porting to classes which desugar this way suggests class declarations should hoist just as functions do to.

# Russell Leggett (12 years ago)

On Thu, Nov 17, 2011 at 3:06 PM, David Flanagan <dflanagan at mozilla.com>wrote:

On 11/16/11 11:45 AM, Erik Arvidsson wrote:

Sorry for being too brief. Today the following works.

f(); ... function f() { ... }

but the following does not:

f(); ... let f = function f() {};

I think it is important that we keep the forward reference behavior with classes. This requires a declarative way of defining classes.

I agree with Erik and Dave that forward references are critical for classes. If we define syntax for classes that is less foward-reference friendly than functions are, it will be a source of bugs (speaking from personal experience) and be perceived as a step backward.

I agree forward references are important, and the way I see it, there are 3 levels of forward reference.

  1. variable hoisting - this can be done with var right now

    var Foo = class {...}

This would allow a function body above it to reference Foo, but not call new Foo() until after it was defined. This is the limit of what Allen's operator proposal could do, even with a declarative form 'class Foo {...}' because it does not create a new function, it depends on the expression given to class.

  1. function hoisting (did I make up this term?) - a function declaration is capable of not only forward reference, but also forward calling, even before the declaration is reached in source order. This is the limit of what current constructor function based class declarations can do. It is also something that my proposed version of the class operator could do, because it always creates a function, and could desugar to the same semantics as the current function style.

  2. class hoisting, where all members of the class are available would require a formal class body instead of an expression, and probably some restrictions on the class body definition. I don't think any form of class proposed so far would allow this.

# Brendan Eich (12 years ago)

On Nov 17, 2011, at 1:34 PM, Russell Leggett wrote:

On Thu, Nov 17, 2011 at 3:06 PM, David Flanagan <dflanagan at mozilla.com> wrote: On 11/16/11 11:45 AM, Erik Arvidsson wrote:

Sorry for being too brief. Today the following works.

f(); ... function f() { ... }

but the following does not:

f(); ... let f = function f() {};

I think it is important that we keep the forward reference behavior with classes. This requires a declarative way of defining classes.

I agree with Erik and Dave that forward references are critical for classes. If we define syntax for classes that is less foward-reference friendly than functions are, it will be a source of bugs (speaking from personal experience) and be perceived as a step backward.

I agree forward references are important, and the way I see it, there are 3 levels of forward reference.

  1. variable hoisting - this can be done with var right now

'let' hoists to block top but it's an error in the current proposal to use the binding before the declaration (with or without an initialiser) has been evaluated.

var Foo = class {...}

This would allow a function body above it to reference Foo, but not call new Foo() until after it was defined. This is the limit of what Allen's operator proposal could do, even with a declarative form 'class Foo {...}' because it does not create a new function, it depends on the expression given to class.

Right.

  1. function hoisting (did I make up this term?)

(You did not -- it's an ancient term and function hoisting always denotes the function being callable at scope top.)

  • a function declaration is capable of not only forward reference, but also forward calling, even before the declaration is reached in source order. This is the limit of what current constructor function based class declarations can do.

And actually do do -- and code counts on this, I believe. It's one of those things about classes-as-sugar that makes me want classes to hoist as functions do (functions in block are not specified by ES1-5 and implemented variously, but in Harmony and ES.next function in block hoists to block-top, both binding and initialization, and so would classes under this point of view).

It is also something that my proposed version of the class operator could do, because it always creates a function, and could desugar to the same semantics as the current function style.

This may seem like a nit-pick, but I think it's not: any variant that sits at statement context in the grammar and has the form 'class C {...}' or 'class D extends B {...}' is not an expression using an operator. It's a class declaration.

  1. class hoisting, where all members of the class are available would require a formal class body instead of an expression, and probably some restrictions on the class body definition. I don't think any form of class proposed so far would allow this.

Why not? Some proposals have either explicitly required (not allowed) this, or at least implied it.

Many proposals mirror function declarations vs. expressions by supporting all of class declarations (hoisted in some proposals as functions are, for the reason given above), named class expresssions (class name is usable in the class body but not head), and anonymous class expressions.

# Russell Leggett (12 years ago)

It is also something that my proposed version of the class operator could do, because it always creates a function, and could desugar to the same semantics as the current function style.

This may seem like a nit-pick, but I think it's not: any variant that sits at statement context in the grammar and has the form 'class C {...}' or 'class D extends B {...}' is not an expression using an operator. It's a class declaration.

Yes, I guess it would not be an operator at that point, but I had thought that much the same way that function can be used as a declaration and an expression, class could be used as a declaration and an operator. Perhaps this is confusing or impossible. Seemed doable to me.

  1. class hoisting, where all members of the class are available would require a formal class body instead of an expression, and probably some restrictions on the class body definition. I don't think any form of class proposed so far would allow this.

Why not? Some proposals have either explicitly required (not allowed) this, or at least implied it.

Yes, sorry. I should have said "To my limited knowledge, ...". I had not seen that as part of any proposal, but I haven't read them all or as thoroughly as I could.

# David Herman (12 years ago)

On Nov 17, 2011, at 10:17 AM, Jason Orendorff wrote:

I'm with Allen. If ES classes can contain any initialization code, I think it should run in program order, interleaved with top-level statements. Anything else is just confusing.

This is a great point, which I'd overlooked (not sure if Allen already said that and I missed it). But I'm not sure whether it argues for no declarative classes, for no syntax for statics, or for a restricted syntax for statics (e.g., only static methods).

Note that classdefs in Ruby and Python aren't hoisted, and nobody complains. In those languages classdefs very often contain procedural code, for many purposes.

Good comparison, thanks. More grist...

# Allen Wirfs-Brock (12 years ago)

On Nov 15, 2011, at 10:27 PM, Russell Leggett wrote:

but given the overall disapproval of the exact syntax of the <| operator, I think its at least a little more obvious what is going on.

Sorry to be coming late to this thread.

I think the above statement is a false characterization to use to start this discussion. There are some who disapprove of <| or other similar special character formulation of this operator. There are other who like it a lot. Certainly there isn't "overall" disapproval. However, we can't really even tell whether there is majority (of what population??) approval or disapproval. At best, all you can legitimately say is that there is not universal approval of the <| token.

# Allen Wirfs-Brock (12 years ago)

On Nov 17, 2011, at 2:05 PM, David Herman wrote:

On Nov 17, 2011, at 10:17 AM, Jason Orendorff wrote:

I'm with Allen. If ES classes can contain any initialization code, I think it should run in program order, interleaved with top-level statements. Anything else is just confusing.

This is a great point, which I'd overlooked (not sure if Allen already said that and I missed it). But I'm not sure whether it argues for no declarative classes, for no syntax for statics, or for a restricted syntax for statics (e.g., only static methods).

Not yet, but here goes. If your hostable class definition contains any sort of evaluable initialization code then you have problems. Such expression might include initializer for "static" properties but they also might include default value initializers for instance properties. Most importantly it includes the determination of the "super class".

let base = oracle.whoIsTheBetterParent(phaseOfMoon); class sub1 extends base {...}; class sub2 extends base { };

or perhaps:

class derived extends Flavors.mixin(nuts, heathbar, fudge) { ...}

All of this is, of course subject, to your specific declaration class design.

If you don't have and never intend to have any declaration components that are evaluated at runtime then hoist away. But if this isn't the case you can't host classes.

(BTW, obviously this isn't just about classes. It applies to any binding form you might come up with in the future)

# Dean Landolt (12 years ago)

On Sat, Nov 19, 2011 at 1:57 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

On Nov 15, 2011, at 10:27 PM, Russell Leggett wrote:

but given the overall disapproval of the exact syntax of the <| operator, I think its at least a little more obvious what is going on.

Sorry to be coming late to this thread.

I think the above statement is a false characterization to use to start this discussion. There are some who disapprove of <| or other similar special character formulation of this operator. There are other who like it a lot. Certainly there isn't "overall" disapproval. However, we can't really even tell whether there is majority (of what population??) approval or disapproval. At best, all you can legitimately say is that there is not universal approval of the <| token.

I was under the impression that TC39 generally operates by consensus. As you note, there are some who disapprove -- if some is a non-inconsequential number wouldn't this constitute overall disapproval?

FWIW I'm indifferent to the syntax, but it reads just fine to me. Still, I'm sure there's something better lurking. Especially if it's true that since it's infix it could be nearly anything. IMHO "begets" is pretty winful.

# John J Barton (12 years ago)

On Sat, Nov 19, 2011 at 11:07 AM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

On Nov 17, 2011, at 2:05 PM, David Herman wrote:

On Nov 17, 2011, at 10:17 AM, Jason Orendorff wrote:

I'm with Allen. If ES classes can contain any initialization code, I think it should run in program order, interleaved with top-level statements. Anything else is just confusing.

This is a great point, which I'd overlooked (not sure if Allen already said that and I missed it). But I'm not sure whether it argues for no declarative classes, for no syntax for statics, or for a restricted syntax for statics (e.g., only static methods).

Not yet, but here goes.  If your hostable class definition contains any sort of evaluable initialization code then you have problems.  Such expression might include initializer for "static" properties but they also might include default value initializers for  instance properties.  Most importantly it includes the determination of the "super class".

let base = oracle.whoIsTheBetterParent(phaseOfMoon); class sub1 extends base {...}; class sub2 extends base { };

or perhaps:

class derived extends Flavors.mixin(nuts, heathbar, fudge) { ...}

All of this is, of course subject, to your specific declaration class design.

If you don't have and never intend to have any declaration components that are evaluated at runtime then hoist away.  But if this isn't the case you can't host classes.

(BTW, obviously this isn't just about classes.  It applies to any binding form you might come up with in the future)

Allen, perhaps a number of issues discussed here on other threads would be clearer if you explain this problem a bit more. You seem to be saying "Don't compute base classes (BTW or anything else)" but I'm unsure.

jjb

# Allen Wirfs-Brock (12 years ago)

On Nov 19, 2011, at 11:38 AM, Dean Landolt wrote:

On Sat, Nov 19, 2011 at 1:57 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

On Nov 15, 2011, at 10:27 PM, Russell Leggett wrote:

but given the overall disapproval of the exact syntax of the <| operator, I think its at least a little more obvious what is going on.

Sorry to be coming late to this thread.

I think the above statement is a false characterization to use to start this discussion. There are some who disapprove of <| or other similar special character formulation of this operator. There are other who like it a lot. Certainly there isn't "overall" disapproval. However, we can't really even tell whether there is majority (of what population??) approval or disapproval. At best, all you can legitimately say is that there is not universal approval of the <| token.

I was under the impression that TC39 generally operates by consensus. As you note, there are some who disapprove -- if some is a non-inconsequential number wouldn't this constitute overall disapproval?

It's true that there isn't consensus within TC39 on the "<|" syntax. However, there also isn't consensus on any other specific token or keyword either. At the May TC39 meeting there was consensus on "advancing to proposal status" the semantic capabilities that were in the enhanced object literal strawman. At the same time it was noted that there wasn't agreement on syntax.

Lack of consensus does not mean "overall disapproval". It means there isn't overall approval. Further consensus building is still required it might be around the pending proposal or it might be around something else.

However, TC39 wan't even mentioned in the statement I took exception to. It seemed to make a broader subjective characterization.

FWIW I'm indifferent to the syntax, but it reads just fine to me. Still, I'm sure there's something better lurking. Especially if it's true that since it's infix it could be nearly anything. IMHO "begets" is pretty winful.

I find keywords such as "begets" a bit more jarring when used as compositional primitives.

To me, "beget" reads fine in isolation such as let bob = tom beget {name: 'Robert');

But when used in combination you get clashes of the underlying natural language meaning of the keywords. Compare let Point = class AbstractPoint begets {... to let Point = class AbstractPoint <| {...

The first form just doesn't "read" quite right to me. For the second form, I don't necessarily internally vocalize a natural language word for <|.

My personal preference is for a special characters based operator based token but it doesn't have to be <|. I've frequently suggested <: as another alternative. I'm less happy with a keyword operator token but I don't reject that alternative and "beget" may well be the best of those.

However, we have to decide on something or we just won't have this functionality at all.

Consensus doesn't mean that everybody likes the solution. It just means that everybody is willing to accept it. What we really need to do is build such a consensus around one of the alternatives.

# Irakli Gozalishvili (12 years ago)

Recently Claus Reinke proposed some alternative that I think feels is so much more natural and simpler than <| or beget:

Instead of this:

foo <| { bar: 'bar' }

prototype can be a special property:

{ prototype: foo, bar: 'bar' }

simple and easy, of course it can have a different name from prototype. Also, this special prototype property can be non-configurable, non-writable and non-enumerable falling back to Object.prototype if not provided. Also Object.create(null, { foo: { value: 'foo' } }) will become as simple as { prototype: null, foo: 'foo' }.

-- Irakli Gozalishvili Web: www.jeditoolkit.com Address: 29 Rue Saint-Georges, 75009 Paris, France (goo.gl/maps/3CHu)

# Brendan Eich (12 years ago)

On Nov 21, 2011, at 5:55 PM, Irakli Gozalishvili wrote:

Recently Claus Reinke proposed some alternative that I think feels is so much more natural and simpler than <| or beget:

Instead of this:

foo <| { bar: 'bar' }

prototype can be a special property:

{ prototype: foo, bar: 'bar' }

Two problems:

  1. Does not work for array, function, and regexp literal right-hand sides.

  2. Preempts a property name. We cannot preempt 'prototype', and 'proto' was already tried (it sucks due to preemption combined with magic meta-programming power in what looks like just another property).

These really won't cut it, and I think the reasons were covered well by Allen already.

# Dmitry Soshnikov (12 years ago)

On 22.11.2011 5:55, Irakli Gozalishvili wrote:

Recently Claus Reinke proposed some alternative that I think feels is so much more natural and simpler than <| or beget:

Yes, it was proposed long time ago:

esdiscuss/2011-May/014391

let foo = {x: 10};

let bar = {

#proto: foo,
#closed

y: 20,
// etc.

};

And the reply was (by Allen)

# Claus Reinke (12 years ago)

Recently Claus Reinke proposed some alternative that I think feels is so much more natural and simpler than <| or beget: .. .. re-interpretation deleted ..

Ahem;-) Glad to see my proposal noticed, but what I really suggested, in

motivating <| as structure representation, not operator
https://mail.mozilla.org/pipermail/es-discuss/2011-November/018626.html

was to interpret

proto <| {..}

as a source representation of the unwritable

{ [[prototype]]: proto , .. }

Two problems:

  1. Does not work for array, function, and regexp literal right-hand sides.

With my original suggestion, could work for array, and regexpr, and function. The difference from the original <| was just that I wanted to interpret <| directly as structure representation, not as an operator taking a full object rhs.

Though I suggested to eliminate the special case for function, and to set only [[prototype]], not 'prototype' as well.

We should be explicit about setting function's [[prototype]] or setting function's 'prototype' (the [[prototype]] for new objects).

  1. Preempts a property name. We cannot preempt 'prototype', and 'proto' was already tried (it sucks due to preemption combined with magic meta-programming power in what looks like just another property).

Not an issue with my original suggestion, as the name is implicit.

Claus clausreinke.github.com

# Brendan Eich (12 years ago)

On Nov 22, 2011, at 9:48 AM, Claus Reinke wrote:

Recently Claus Reinke proposed some alternative that I think feels is so much more natural and simpler than <| or beget: .. .. re-interpretation deleted ..

Ahem;-) Glad to see my proposal noticed, but what I really suggested, in

motivating <| as structure representation, not operator esdiscuss/2011-November/018626

Nothing there about arrays or regular expressions, and the function examples do not show anything like

superFun <| function (...) {...}

Instead of Ahem'ing, could you cite what you claim is a prior proposal with specifics showing how it addresses object, array, regexp and function [[Prototype]] presetting?

# Bob Nystrom (12 years ago)

On Tue, Nov 22, 2011 at 10:41 AM, Brendan Eich <brendan at mozilla.com> wrote:

Nothing there about arrays or regular expressions, and the function examples do not show anything like

superFun <| function (...) {...}

Instead of Ahem'ing, could you cite what you claim is a prior proposal with specifics showing how it addresses object, array, regexp and function [[Prototype]] presetting?

I'm guessing this been proposed before but I think you could handle all of those except RegExp with syntax something like:

// object: { extends someObj, prop1: value, prop2: value }

// array: [ extends someObj, value1, value2 ]

// function declaration: function foo(arg) extends someObj { ... }

// function expression: function(arg) extends someObj { ... }

You could maybe cram it into RegExp with something like:

/pattern/ extends someObj;

I haven't been following the <| discussion closely (it seems like it's in good hands and has lots of smart people poking at it) so forgive me if this was proposed and rejected for valid reasons while I wasn't looking. :)

# Quildreen Motta (12 years ago)

2011/11/22 Bob Nystrom <rnystrom at google.com>

On Tue, Nov 22, 2011 at 10:41 AM, Brendan Eich <brendan at mozilla.com>wrote:

Nothing there about arrays or regular expressions, and the function examples do not show anything like

superFun <| function (...) {...}

Instead of Ahem'ing, could you cite what you claim is a prior proposal with specifics showing how it addresses object, array, regexp and function [[Prototype]] presetting?

I'm guessing this been proposed before but I think you could handle all of those except RegExp with syntax something like:

// object: { extends someObj, prop1: value, prop2: value }

// array: [ extends someObj, value1, value2 ]

// function declaration: function foo(arg) extends someObj { ... }

// function expression: function(arg) extends someObj { ... }

You could maybe cram it into RegExp with something like:

/pattern/ extends someObj;

I haven't been following the <| discussion closely (it seems like it's in good hands and has lots of smart people poking at it) so forgive me if this was proposed and rejected for valid reasons while I wasn't looking. :)

From what I get of Claus' proposal, this is more or less what he's been

proposing.

Though he seemed to be proposing it from a type-decl/declarative stand point:

type X :: JSObject (Proto proto) thing

to be roughy equivalent to `proto <| thing'.

which would be in turn roughly equivalent to the imperative form:

thing = {} | /regex/ | function(){ } | [] | 1 | "a" // <- don't mind my lazyness and treat this as several different kinds of possible objects in the next statement thing.[[prototype]] = proto

So, the <| syntax would be a declarative form that composes something of the type in the RHS, having a prototype as LHS, thus working for any kind of object -- just as proto does.

Then he proposed using the same syntax for doing pattern matching on the prototype chain, which sounds quite cool.

# Allen Wirfs-Brock (12 years ago)

On Nov 22, 2011, at 10:54 AM, Bob Nystrom wrote:

On Tue, Nov 22, 2011 at 10:41 AM, Brendan Eich <brendan at mozilla.com> wrote:

Nothing there about arrays or regular expressions, and the function examples do not show anything like

superFun <| function (...) {...}

Instead of Ahem'ing, could you cite what you claim is a prior proposal with specifics showing how it addresses object, array, regexp and function [[Prototype]] presetting?

I'm guessing this been proposed before but I think you could handle all of those except RegExp with syntax something like:

// object: { extends someObj, prop1: value, prop2: value }

// array: [ extends someObj, value1, value2 ]

// function declaration: function foo(arg) extends someObj { ... }

// function expression: function(arg) extends someObj { ... }

You could maybe cram it into RegExp with something like:

/pattern/ extends someObj;

I haven't been following the <| discussion closely (it seems like it's in good hands and has lots of smart people poking at it) so forgive me if this was proposed and rejected for valid reasons while I wasn't looking. :)

You (and others) are roughly working backwards along the path that ultimately led to the invention of <|.

Some of the concerns about the "embedded" approach:

You are introducing several syntactic forms that have to be learned an remembered instead of just one.

Your examples are only consider examples where the "proto" value is a simple identifier reference:

function (arg) extends {extends Function.prototype, method1: function() {}, method2() {}} { ...}

contrast with Function.prototype <| {method1: function() {}, method2: function() {}} <| function (arg) {...}

To handle RegExps you have to introduce something that looks like an operator but is even more specialized than <|.

You can't handle numeric or string literals. This may seem like a stretch, but consider:

UTF16StringProto <| "a string containing UTF 16 surrogate pairs"

where UTF16StringProto has versions of the string methods that understand how to treat surrogate pairs as logically single characters.

A pseudo binary operator like <| (whatever your preferred spelling) still seems to me like the most directly and least intrusive way to deal with all of these scenarios.

# Claus Reinke (12 years ago)

Recently Claus Reinke proposed some alternative that I think feels is so much more natural and simpler than <| or beget: .. .. re-interpretation deleted ..

Ahem;-) Glad to see my proposal noticed, but what I really suggested, in

motivating <| as structure representation, not operator esdiscuss/2011-November/018626 ..

Nothing there about arrays or regular expressions, and the function examples do not show anything like

superFun <| function (...) {...}

Instead of Ahem'ing, could you cite what you claim is a prior proposal with specifics showing how it addresses object, array, regexp and function [[Prototype]] presetting?

My proposal was no separate proposal in the ES wiki sense, but a suggestion about re-interpreting and modifying the existing proposal about <| . Specifically, it

was to interpret

proto <| {..}

as a source representation of the unwritable

{ [[prototype]]: proto , .. }

As such, it inherits most aspects of the original proposal. In brief,

proto <| literal

is the object generated from literal, with [[prototype]] set to proto, where the literal can be any object-generating literal (object, array, regexp, or function).

The two modifications to the original proposal were:

  • <| structurally represents an unnameable property; so it does not need to modify its rhs, it only applies to literals, and it can be used in destructuring

  • <| only makes the [[prototype]] property representable; so there is no special case for 'proto <| function', all literals are treated the same way;

    instead of modifying 'function's 'prototype' property, one would write 'function(..){..; return super <| {..}}'

Claus clausreinke.github.com