Is class syntax really necessary ?
On 23.05.2011 14:17, Irakli Gozalishvili wrote:
Hi,
I think there lot's of proposals for ES.next that require syntax extensions, which is probably worth if new functionality added or shortens most commonly used constructs like functions (were no other option is available). In case of this proposal: strawman:classes_with_trait_composition#open_issues even though I like it I'm not sure adding new syntax is worth it.
May I ask a counter question -- why do you think it's not good to add syntactic sugar for classes? It's a kind of a strange thing. People sometimes talk about unnecessarily of a sugar. But why I'm asking? Is it bad to use a sugar? Or do you really worry about an implementation that e.g. a language will be "too heavy"? After all, it's not even the issue of users, it's the issue of implementers.
That is, since it's just a sugar, the users still are free to use
desugared constructions (i.e. constructor + prototype + manual linkage
of parent prototype in case of inheritance). If they want to. If they
instead want to write with the sugar -- why we should worry about that
"adding of new syntax is worth" (taking into account that class
,
extends
, etc. keywords are already reserved since ES3 era).
So from what I can tell, right after the sugar for classes is standardized, everyone will just start to use it and forget about desugared constructions. And nobody even will think and bother about whether it's worth or not.
A good point of standardizing this "wrapper" (which is just a sugar) is
that all ad-hoc class-wrappers of libraries will be just eliminated and
there will be common classes sugar "from the box". Since JS already
supports classical inheritance (though, without sugar), I don't how it's
bad not to standardize the sugar for it. It will be convenient who need
to program a class-based system. At the same time, if someone will still
need a "chaotic code reuse", i.e. a prototype-based inheritance ("reuse
a code from that object from which I want") -- they still be able to use
things as Object.create
(or <| operator, etc.)
I'm not suggesting that sugar for class composition is not necessary, example from three.js mrdoob/three.js/blob/master/src/objects/SkinnedMesh.js used by proposal highlights necessity pretty very well, I'm just thinking of doing that without introducing new syntax, here is one option: gist.github.com/986487
This way syntax noise may be reduced in addition this can be shimmed into current JS by implementing
Function.prototype.extend
.
Yeah, OTOH, it can be so too. Though, the familiar sugar will be bring the ability to involve programmers quicker.
Also every single frameworks today does something similar in one form or another, IMO all is necessary is to have a standard that will let bikeshedding go away.
Right.
Dmitry.
On Monday, 2011-05-23 at 13:10 , Dmitry A. Soshnikov wrote:
On 23.05.2011 14:17, Irakli Gozalishvili wrote:
Hi,
I think there lot's of proposals for ES.next that require syntax extensions, which is probably worth if new functionality added or shortens most commonly used constructs like functions (were no other option is available). In case of this proposal: strawman:classes_with_trait_composition#open_issues even though I like it I'm not sure adding new syntax is worth it.
May I ask a counter question -- why do you think it's not good to add syntactic sugar for classes? It's a kind of a strange thing. People sometimes talk about unnecessarily of a sugar. But why I'm asking? Is it bad to use a sugar? Or do you really worry about an implementation that e.g. a language will be "too heavy"? After all, it's not even the issue of users, it's the issue of implementers.
Dimitry thanks that's very good question.
- More syntax means larger language surface, which adds complexity more things to remember / learn. More things to consider in ES.next.next
- I OOP in JS is already confusing for people coming from other languages, this proposal will make it even more confusing.
That is, since it's just a sugar, the users still are free to use desugared constructions (i.e. constructor + prototype + manual linkage of parent prototype in case of inheritance). If they want to. If they instead want to write with the sugar -- why we should worry about that "adding of new syntax is worth" (taking into account that class
, extends
, etc. keywords are already reserved since ES3 era).
So from what I can tell, right after the sugar for classes is standardized, everyone will just start to use it and forget about desugared constructions. And nobody even will think and bother about whether it's worth or not.
I don't doubt that, but adoption will take longer.
On May 23, 2011, at 6:11 AM, Irakli Gozalishvili wrote:
On Monday, 2011-05-23 at 13:10 , Dmitry A. Soshnikov wrote:
On 23.05.2011 14:17, Irakli Gozalishvili wrote:
Hi,
I think there lot's of proposals for ES.next that require syntax extensions, which is probably worth if new functionality added or shortens most commonly used constructs like functions (were no other option is available). In case of this proposal: strawman:classes_with_trait_composition#open_issues even though I like it I'm not sure adding new syntax is worth it.
May I ask a counter question -- why do you think it's not good to add syntactic sugar for classes? It's a kind of a strange thing. People sometimes talk about unnecessarily of a sugar. But why I'm asking? Is it bad to use a sugar? Or do you really worry about an implementation that e.g. a language will be "too heavy"? After all, it's not even the issue of users, it's the issue of implementers.
Dimitry thanks that's very good question.
- More syntax means larger language surface, which adds complexity more things to remember / learn. More things to consider in ES.next.next
It's true, although not everyone learns it all up front. Especially where new syntax is not yet supported in all browsers, and the student is not using a compiler to translate new to old version.
I think the sharper version of your (1) is: "class syntax is too much syntax to solve the problems people have with prototypal inheritance: subclassed prototype/constructor set-up and super calls."
I agree with that. Allen had been proposing super support that was separate. You proposed Function.prototype.extend. Perhaps there's enough there to relieve us of the large, tradition-haunted ediface of classes.
- I OOP in JS is already confusing for people coming from other languages, this proposal will make it even more confusing.
This is not as on target, in my opinion, because even if overwrought, classes are trying to solve some confusion or accident hazards in JS today to-do with prototypal inheritance. Namely,
(A) the boilerplate needed to set up a sub-prototype object with correct constructor property, and
(B) the pain of doing correct super calls by hand.
Can we agree that these are problems to be solved, if not by classes then by other APIs or special forms?
On May 23, 2011, at 8:31 AM, Brendan Eich wrote:
On May 23, 2011, at 6:11 AM, Irakli Gozalishvili wrote:
- More syntax means larger language surface, which adds complexity more things to remember / learn. More things to consider in ES.next.next
It's true, although not everyone learns it all up front. Especially where new syntax is not yet supported in all browsers, and the student is not using a compiler to translate new to old version.
I think the sharper version of your (1) is: "class syntax is too much syntax to solve the problems people have with prototypal inheritance: subclassed prototype/constructor set-up and super calls."
I agree with that.
To be fair, others want to hang more hats on class syntax, including traits (separate proposal) and const/abstract modifiers. Then there are the guards and trademarking strawmen.
Class syntax is like a lint brush for such features. If we add it, it will accrete more semantics (with unambiguous syntax, I hope) over time. This is just inevitable, in my view. It makes me want to resist classes and look at smaller and more direct fixes for the two known prototypal hazards.
On Monday, 2011-05-23 at 17:31 , Brendan Eich wrote: On May 23, 2011, at 6:11 AM, Irakli Gozalishvili wrote:
On Monday, 2011-05-23 at 13:10 , Dmitry A. Soshnikov wrote:
On 23.05.2011 14:17, Irakli Gozalishvili wrote:
Hi,
I think there lot's of proposals for ES.next that require syntax extensions, which is probably worth if new functionality added or shortens most commonly used constructs like functions (were no other option is available). In case of this proposal: strawman:classes_with_trait_composition#open_issues even though I like it I'm not sure adding new syntax is worth it.
May I ask a counter question -- why do you think it's not good to add syntactic sugar for classes? It's a kind of a strange thing. People sometimes talk about unnecessarily of a sugar. But why I'm asking? Is it bad to use a sugar? Or do you really worry about an implementation that e.g. a language will be "too heavy"? After all, it's not even the issue of users, it's the issue of implementers.
Dimitry thanks that's very good question.
- More syntax means larger language surface, which adds complexity more things to remember / learn. More things to consider in ES.next.next
It's true, although not everyone learns it all up front. Especially where new syntax is not yet supported in all browsers, and the student is not using a compiler to translate new to old version.
I think the sharper version of your (1) is: "class syntax is too much syntax to solve the problems people have with prototypal inheritance: subclassed prototype/constructor set-up and super calls."
I agree with that. Allen had been proposing super support that was separate. You proposed Function.prototype.extend. Perhaps there's enough there to relieve us of the large, tradition-haunted ediface of classes.
- I OOP in JS is already confusing for people coming from other languages, this proposal will make it even more confusing.
This is not as on target, in my opinion, because even if overwrought, classes are trying to solve some confusion or accident hazards in JS today to-do with prototypal inheritance. Namely,
(A) the boilerplate needed to set up a sub-prototype object with correct constructor property, and
(B) the pain of doing correct super calls by hand.
Can we agree that these are problems to be solved, if not by classes then by other APIs or special forms?
I do completely agree with that!
On Mon, May 23, 2011 at 08:51, Brendan Eich <brendan at mozilla.com> wrote:
Class syntax is like a lint brush for such features. If we add it, it will accrete more semantics (with unambiguous syntax, I hope) over time. This is just inevitable, in my view. It makes me want to resist classes and look at smaller and more direct fixes for the two known prototypal hazards.
Yes, please!
I assume "two known hazards" is referring to your previous email: "subclassed prototype/constructor set-up and super calls."
I've been using this pattern in my OOP javascript programs lately: isaacs/inherits/blob/master/inherits.js
It works really well, behaves as expected (easy for the author to say, ha, but it doesn't violate instanceof, doesn't call ctors more than once, doesn't clobber already-added prototype properties, etc.) And it's about 10 lines, easy to read and grok.
What would make it even nicer, however, with minimal added syntax:
- Call "super(a, b, c)" instead of "Child.super.call(this, a, b, c)"
- Maybe Parent.extend(Child) or Child.inherit(Parent) instead of inherits(Child, Parent)
- Right now, calling parent versions of overridden methods is a painful: "this.constructor.super.prototype.someMethod.call(this)". It'd be nice to use something like "super.someMethod()".
On Monday, 2011-05-23 at 18:14 , Isaac Schlueter wrote:
On Mon, May 23, 2011 at 08:51, Brendan Eich <brendan at mozilla.com> wrote:
Class syntax is like a lint brush for such features. If we add it, it will accrete more semantics (with unambiguous syntax, I hope) over time. This is just inevitable, in my view. It makes me want to resist classes and look at smaller and more direct fixes for the two known prototypal hazards.
Yes, please!
I assume "two known hazards" is referring to your previous email: "subclassed prototype/constructor set-up and super calls."
I've been using this pattern in my OOP javascript programs lately: isaacs/inherits/blob/master/inherits.js
It works really well, behaves as expected (easy for the author to say, ha, but it doesn't violate instanceof, doesn't call ctors more than once, doesn't clobber already-added prototype properties, etc.) And it's about 10 lines, easy to read and grok.
I personally prefer backbone style as IMO it has advantage of keeping complete setup in one declaration statement.
On Mon, May 23, 2011 at 10:11 AM, Irakli Gozalishvili <rfobic at gmail.com> wrote:
On Monday, 2011-05-23 at 13:10 , Dmitry A. Soshnikov wrote:
On 23.05.2011 14:17, Irakli Gozalishvili wrote:
Hi,
I think there lot's of proposals for ES.next that require syntax extensions, which is probably worth if new functionality added or shortens most commonly used constructs like functions (were no other option is available). In case of this proposal: strawman:classes_with_trait_composition#open_issues even though I like it I'm not sure adding new syntax is worth it.
May I ask a counter question -- why do you think it's not good to add syntactic sugar for classes? It's a kind of a strange thing. People sometimes talk about unnecessarily of a sugar. But why I'm asking? Is it bad to use a sugar? Or do you really worry about an implementation that e.g. a language will be "too heavy"? After all, it's not even the issue of users, it's the issue of implementers.
Dimitry thanks that's very good question.
- More syntax means larger language surface, which adds complexity more things to remember / learn. More things to consider in ES.next.next
- I OOP in JS is already confusing for people coming from other languages, this proposal will make it even more confusing.
As a JS developer who works daily with the language, I can tell you that
pretty much everyone I know that doesn't work exclusively with JavaScript hates the language (and are very emotional and vocal about it) because of:
- The DOM
- Prototype based inheritance
And the reasons why they don't like prototype inheritance are:
- They don't understand it. Chaining prototypes is not intuitive. This is solved by your Function.prototype.extend proposal.
- The beginner examples around the web and their verbosity
- The lack of keywords commonly found in other languages to refer to OOP concepts. At least in my country, this is a problem because 99% of the professional/high school training on programming is based on heavily typed OOP languages. The other reason for this situation is that JavaScript is mostly learned out of necessity, not choice. This means most JS users come from back-end experience (I saw a tweet a couple of days ago: "Mullet developer: strong back-end skills, weak front-end skills"), where they have a choice of platform.
Most beginner examples start with:
function Person(name) { this.name = name; } Person.prototype.speak = function () { /* ... / } Person.prototype.walk = function() { / ... */ }
Most beginners react with "this sucks!" just based on the syntax, not because of the semantics. New syntax for constructor and prototypes would be very useful for beginners to learn gradually how to use the prototypical nature of the language, and for advanced programmers that use OOP to use what they already use with less noise. IMHO this is a case in which syntax reduces complexity. We also need to start writing better examples in our blogs and wikis.
There is one aspect in which I do agree it can be more confusing. There are some keywords from other languages being reused in a completely different fashion. Mostly those related to property access privileges. Using "public" to refer to an instance property seems totally weird to me.
Juan
Sugar is fine for defining classes (as opposed to defining types in terms of the constructor) but I get a little worried when I see the 'extends' keyword. I'm probably biased but I see many JavaScript trainees eager to simulate classical inheritance because it fits right in their comfort zone. It's been said a million times before but one of the most appealing characteristics of the JavaScript language is the availability of so many alternatives to rigid inheritance patterns (e.g. mixins, delegation, composition) which can liberate the developer from the confines of the traditional 'class' hierarchy and promote more flexible and readable code.
If Dmitry is right in saying once class + extends is available "everyone will just start to use it" I'm concerned that this may be to the detriment of the language.
thanks Angus
The class syntax would be a great boon to the Closure Compiler. Much of ADVANCED mode optimizations depends on understanding class relationships, currently this means "teaching" it about each framework's "extend" or "inherit" methods and each of their subtleties.
On Monday, 2011-05-23 at 18:47 , John Lenz wrote:
The class syntax would be a great boon to the Closure Compiler. Much of ADVANCED mode optimizations depends on understanding class relationships, currently this means "teaching" it about each framework's "extend" or "inherit" methods and each of their subtleties.
If there will be build-in Function.prototype.extend
it will be pretty easy to teach closure compiler to take advantage of that.
On May 23, 2011, at 8:31 AM, Brendan Eich wrote:
On May 23, 2011, at 6:11 AM, Irakli Gozalishvili wrote:
On Monday, 2011-05-23 at 13:10 , Dmitry A. Soshnikov wrote:
On 23.05.2011 14:17, Irakli Gozalishvili wrote:
Hi,
I think there lot's of proposals for ES.next that require syntax extensions, which is probably worth if new functionality added or shortens most commonly used constructs like functions (were no other option is available). In case of this proposal: strawman:classes_with_trait_composition#open_issues even though I like it I'm not sure adding new syntax is worth it.
May I ask a counter question -- why do you think it's not good to add syntactic sugar for classes? It's a kind of a strange thing. People sometimes talk about unnecessarily of a sugar. But why I'm asking? Is it bad to use a sugar? Or do you really worry about an implementation that e.g. a language will be "too heavy"? After all, it's not even the issue of users, it's the issue of implementers.
Dimitry thanks that's very good question.
- More syntax means larger language surface, which adds complexity more things to remember / learn. More things to consider in ES.next.next
It's true, although not everyone learns it all up front. Especially where new syntax is not yet supported in all browsers, and the student is not using a compiler to translate new to old version.
I think the sharper version of your (1) is: "class syntax is too much syntax to solve the problems people have with prototypal inheritance: subclassed prototype/constructor set-up and super calls."
I agree with that. Allen had been proposing super support that was separate. You proposed Function.prototype.extend. Perhaps there's enough there to relieve us of the large, tradition-haunted ediface of classes.
- I OOP in JS is already confusing for people coming from other languages, this proposal will make it even more confusing.
This is not as on target, in my opinion, because even if overwrought, classes are trying to solve some confusion or accident hazards in JS today to-do with prototypal inheritance. Namely,
(A) the boilerplate needed to set up a sub-prototype object with correct constructor property, and
(B) the pain of doing correct super calls by hand.
I hope we can add the hazards of incorrectly adding mutable state to a prototype and not an instance to this list. I.e., many people get this wrong:
function C(){ // should include: // this._list = []; } C.prototype = { _list: [], addToList: function(item) { this._list.push(item); // logic error! // side-effects here } };
Allen and I have discussed this before, so it's not a surprise, but I'm worried that some proposals might omit fixing this on their todo list.
Can we agree that these are problems to be solved, if not by classes then by other APIs or special forms?
/be
es-discuss mailing list es-discuss at mozilla.org, mail.mozilla.org/listinfo/es-discuss
-- Alex Russell slightlyoff at google.com slightlyoff at chromium.org alex at dojotoolkit.org BE03 E88D EABB 2116 CC49 8259 CF78 E242 59C3 9723
On May 23, 2011, at 9:45 AM, Angus Croll wrote:
Sugar is fine for defining classes (as opposed to defining types in terms of the constructor) but I get a little worried when I see the 'extends' keyword. I'm probably biased but I see many JavaScript trainees eager to simulate classical inheritance because it fits right in their comfort zone. It's been said a million times before but one of the most appealing characteristics of the JavaScript language is the availability of so many alternatives to rigid inheritance patterns (e.g. mixins, delegation, composition) which can liberate the developer from the confines of the traditional 'class' hierarchy and promote more flexible and readable code.
Sorry, but I'm not buying it. That same flexibility leads to the tower of babel, and in any case, nobody is looking to remove any of that power with the proposals on offer. They're simply trying to pave the proverbial cow-paths. Don't hold up the train for a perceived slight...but do yell if you think something is actually being taken away.
If Dmitry is right in saying once class + extends is available "everyone will just start to use it" I'm concerned that this may be to the detriment of the language.
thanks Angus
On Mon, May 23, 2011 at 9:14 AM, Isaac Schlueter <i at izs.me> wrote:
On Mon, May 23, 2011 at 08:51, Brendan Eich <brendan at mozilla.com> wrote:
Class syntax is like a lint brush for such features. If we add it, it will accrete more semantics (with unambiguous syntax, I hope) over time. This is just inevitable, in my view. It makes me want to resist classes and look at smaller and more direct fixes for the two known prototypal hazards.
Yes, please!
I assume "two known hazards" is referring to your previous email: "subclassed prototype/constructor set-up and super calls."
I've been using this pattern in my OOP javascript programs lately: isaacs/inherits/blob/master/inherits.js
It works really well, behaves as expected (easy for the author to say, ha, but it doesn't violate instanceof, doesn't call ctors more than once, doesn't clobber already-added prototype properties, etc.) And it's about 10 lines, easy to read and grok.
What would make it even nicer, however, with minimal added syntax:
- Call "super(a, b, c)" instead of "Child.super.call(this, a, b, c)"
- Maybe Parent.extend(Child) or Child.inherit(Parent) instead of inherits(Child, Parent)
- Right now, calling parent versions of overridden methods is a painful: "this.constructor.super.prototype.someMethod.call(this)". It'd be nice to use something like "super.someMethod()".
es-discuss mailing list es-discuss at mozilla.org, mail.mozilla.org/listinfo/es-discuss
es-discuss mailing list es-discuss at mozilla.org, mail.mozilla.org/listinfo/es-discuss
-- Alex Russell slightlyoff at google.com slightlyoff at chromium.org alex at dojotoolkit.org BE03 E88D EABB 2116 CC49 8259 CF78 E242 59C3 9723
On May 23, 2011, at 10:03 AM, Alex Russell wrote:
(A) the boilerplate needed to set up a sub-prototype object with correct constructor property, and
(B) the pain of doing correct super calls by hand.
I hope we can add the hazards of incorrectly adding mutable state to a prototype and not an instance to this list. I.e., many people get this wrong:
function C(){ // should include: // this._list = []; } C.prototype = { _list: [], addToList: function(item) { this._list.push(item); // logic error! // side-effects here } };
Allen and I have discussed this before, so it's not a surprise, but I'm worried that some proposals might omit fixing this on their todo list.
That's a good (C) but I do not see it addressed. Indeed Bob Nystrom counted occurrences in one codebase and found default property values set in class prototypes.
Your point seems to be that mutable (object) default values are a mistake, and I tend to agree. But how to exclude this option? Just adding sugar won't prevent such mistakes, indeed it could make the mistake more likely, depending on ergonomics and more imponderable user factors.
Where and how does the current proposal address (C)?
On May 23, 2011, at 10:41 AM, Brendan Eich wrote:
On May 23, 2011, at 10:03 AM, Alex Russell wrote:
(A) the boilerplate needed to set up a sub-prototype object with correct constructor property, and
(B) the pain of doing correct super calls by hand.
I hope we can add the hazards of incorrectly adding mutable state to a prototype and not an instance to this list. I.e., many people get this wrong:
function C(){ // should include: // this._list = []; } C.prototype = { _list: [], addToList: function(item) { this._list.push(item); // logic error! // side-effects here } };
Allen and I have discussed this before, so it's not a surprise, but I'm worried that some proposals might omit fixing this on their todo list.
That's a good (C) but I do not see it addressed. Indeed Bob Nystrom counted occurrences in one codebase and found default property values set in class prototypes.
Your point seems to be that mutable (object) default values are a mistake, and I tend to agree. But how to exclude this option? Just adding sugar won't prevent such mistakes, indeed it could make the mistake more likely, depending on ergonomics and more imponderable user factors.
Where and how does the current proposal address (C)?
I don't think it does explicitly. The repair would be a hidden init sort of method that does property default initialization on instances before the user-provided constructor is called.
In talking with Peter and Arv, it sounded like they thought this was the sort of thing we'd be able to hammer out once the strawman reaches proposal (and I agreed), although I do think it's worth calling out explicitly in the strawman too.
/be
Can we agree that these are problems to be solved, if not by classes then by other APIs or special forms?
/be
es-discuss mailing list es-discuss at mozilla.org, mail.mozilla.org/listinfo/es-discuss
-- Alex Russell slightlyoff at google.com slightlyoff at chromium.org alex at dojotoolkit.org BE03 E88D EABB 2116 CC49 8259 CF78 E242 59C3 9723
es-discuss mailing list es-discuss at mozilla.org, mail.mozilla.org/listinfo/es-discuss
-- Alex Russell slightlyoff at google.com slightlyoff at chromium.org alex at dojotoolkit.org BE03 E88D EABB 2116 CC49 8259 CF78 E242 59C3 9723
- More syntax means larger language surface, which adds complexity more things to remember / learn. More things to consider in ES.next.next
Yup, languages almost always tend to get bigger over time since it's really hard to remove features.
For me, the goal isn't to make the language as small as possible, it's to make it as powerful as possible where I define "power" as "expressiveness / size". If a new feature adds a lot of expressiveness (and I do believe our class proposal does), then to me that's a win.
- I OOP in JS is already confusing for people coming from other languages, this proposal will make it even more confusing.
It is very confusing. My belief is that it's confusing mainly because you're forced to dig into the underlying machinery to do OOP in JS instead of having a declarative form that reflects your intent.
Say you're thinking "I want to define a kind of Person object where each person has a name and they have a method where they say their name." In JS, you have to then know the underlying prototype machinery required to achieve that intent:
function Person(name) { // Why does a function define a kind of thing? this.name; }
Person.prototype.say = function() { // Where did this magic prototype object come from? log(this.name); // How is "this" bound to an instance of a person here? }
It's good to have access to this low-level stuff when you need it, but forcing users to go through that all the time is I think where the confusion comes from. It's a bit like if you had to open the hood and reach into the engine every time you needed to change gears. It works, but it's not what I'd call usable. A good class syntax can essentially be a nice simple gearshift level that users can access from the comfort of their seat:
class Person { constructor(name) { this.name = name; } say() { log(this.name); } }
From my perspective main problem today is verbosity, which can be fixed without introducing new syntax. Also while I named some disadvantages, I can't really see any advantages of dedicated syntax, there for I think it's better to avoid adding more syntax changes.
I definitely like the idea of minimizing new syntax and taking advantage of JS's existing expressiveness. But there are some things that dedicated syntax can give you. With current class patterns in JS, the language itself has no idea textually that you're making a class. That limits the terseness it can provide to you.
For example, the current proposal (and a little extra sugar in Traceur's flavor of it) let you do this:
class FramedButton extends Button { paint() { drawFrame(class.thickness); super.paint(); // Call base class version. }
static const thickness = 3; }
Here, super desugars to FramedButton.prototype.paint.call(this); This is terse, less error-prone, and avoids mentioning the base class by name which makes it easier to refactor hierarchies.
Likewise, class.thickness desugars to FramedButton.thickness. This is again terse and friendlier towards refactoring.
The reason we can make that work is because where those super and class.calls appear, we can textually tell that we're inside a class definition so we know what to desugar to. Existing class patterns that work imperatively don't have that luxury.
An HTML attachment was scrubbed... URL: esdiscuss/attachments/20110523/ec7b513a/attachment-0001
On Mon, May 23, 2011 at 8:51 AM, Brendan Eich <brendan at mozilla.com> wrote:
Class syntax is like a lint brush for such features. If we add it, it will accrete more semantics (with unambiguous syntax, I hope) over time. This is just inevitable, in my view. It makes me want to resist classes and look at smaller and more direct fixes for the two known prototypal hazards.
I think you're point is valid, though a shade more negatively tinged than I'd like. :)
Class syntax probably will/would gain additional features over time, but I'd like to believe that it would only gain features that we all agree are useful and worth their weight in text. I have good faith that we won't just start slapping new stuff in there willy-nilly. Given that ES's evolution is consensus-based (and it's based on the consensus of people who all like that ES is small and simple!) and dependent on clients upgrading, I'm not too worried that things are going to go all Algol-68 on us.
What I do worry about is that ES won't "scale" as quickly as the problems we're solving with it. People are writing huge codebases in Javascript now (Gmail! Flickr! Google Maps!) The only way programs of that size can be managed by our primitive monkey brains is if we can write code at a higher level of abstraction. For me, declarative class syntax is a really helpful tool for doing that.
It's not just about less typing. It's about less distance between intent and code. That distance has to be covered by everyone reading code, so I figure the more we can shorten it, the more quickly we can get things done.
On Mon, May 23, 2011 at 10:41 AM, Brendan Eich <brendan at mozilla.com> wrote:
On May 23, 2011, at 10:03 AM, Alex Russell wrote:
(A) the boilerplate needed to set up a sub-prototype object with correct constructor property, and
(B) the pain of doing correct super calls by hand.
I hope we can add the hazards of incorrectly adding mutable state to a prototype and not an instance to this list. I.e., many people get this wrong:
function C(){ // should include: // this._list = []; } C.prototype = { _list: [], addToList: function(item) { this._list.push(item); // logic error! // side-effects here } };
There's two bugs there:
- Not creating this._list in the ctor.
- Creating one on .prototype.
If bug #1 is fixed, there's no sensible reason to ever define _list on the prototype since it would always be shadowed.
Where and how does the current proposal address (C)?
One thing I'd like the proposal to support, which it doesn't currently, is initializers on instance property declarations. Then you could do:
class C { public _list = []; }
With that, you'll correctly get a new _list on each instance of C when it's created.
Using "public" to refer to an instance property seems totally weird to me.
For what it's worth, I agree. I'd prefer var or instance. I've already seen at least one example of someone misinterpreting it and doing something like:
class C { public someMethod() { ... } }
Their intent was to define a method on C's prototype (like you usually do) and have it be publicly accessible (like they usually are), but the above syntax doesn't do that. It's, I believe, an error instead because you're trying to define a per-instance method and it doesn't allow per-instance initializers.
I'm pretty sure most people reading the above code would not interpret it the way the proposal does.
there are lots of ES.next features that let us do something that we could not do at all previously (weak tables and refs are a good example). features that enable new kinds of applications we couldn't previous build.
+1 on this point. There’s been a lot of discussion of syntactic sugar recently, but much less discussion of the features that provide new capabilities, or new opportunities for meaningful performance enhancements that make new kinds of applications possible. Features like private names as a library feature, String and Math enhancements, proxies, runtime capabilities for module loading, typed arrays, and others are incremental enabling value which unblocks or makes realistic things which aren’t currently possible. These are also features that are more likely to be implemented sooner, and which developers can adopt more quickly and smoothly via feature-detection in 'text/javascript'.
Luke
If one has a class proposal with the prerequisite examples and grammar specification, how does one propose this with a possible goal of an alternate strawman?
On Mon, May 23, 2011 at 11:32 AM, Bob Nystrom <rnystrom at google.com> wrote:
Using "public" to refer to an instance property seems totally weird to me.
For what it's worth, I agree. I'd prefer var or instance. I've already seen at least one example of someone misinterpreting it and doing something like:
class C { public someMethod() { ... } }
Their intent was to define a method on C's prototype (like you usually do) and have it be publicly accessible (like they usually are), but the above syntax doesn't do that. It's, I believe, an error instead because you're trying to define a per-instance method and it doesn't allow per-instance initializers.
I'm pretty sure most people reading the above code would not interpret it the way the proposal does.
Just to be clear, the current proposal reject this as an early syntax error. It is only if we extend the public declarations to allow out-of-constructor initializations that we would get into this specific trouble.
Le 23/05/2011 20:15, Mikeal Rogers a écrit :
this is going to get a little philosophical and not super technical so i apologize in advance.
i don't agree that expressiveness is necessarily a good thing. expressiveness comes with a cognitive overhead when reading and thinking about code, it's in your head, always. the more feature, the more language semantics, the more you have to keep in your head before you even start adding the lines of code you're keeping in there.
I disagree with you on your generalization: "a += 1" is more expressive than "a = a+1" and I don't see a cognitive overhead is the former, maybe the opposite, it helps the reader to understand more easily understand the writer's intent. Exact same thing when replacing a for-loop traversing an array with a .forEach/.map/.filter/etc. call. They are a very good way to be expressive and show an intent which is easy to understand both from the interpreter perspective and human reader perspective.
saving 4 lines of code with new language semantics might save 4 lines, but those were 4 lines in one place that i only had to think about in that spot. the new expressiveness i have to carry around with me whenever i think about any code.
I however agree that adding new syntax just for the sake of saving a couple of lines isn't a good idea. And I have to admit that so far, I am not convinced by the benefits of the class syntax in terms of abstraction.
i don't think it's a win to add semantics to the language that accomplish something we can already do. there are lots of ES.next features that let us do something that we could not do at all previously (weak tables and refs are a good example). features that enable new kinds of applications we couldn't previous build.
i said it at NodeConf and I'll say it again here, nobody ever said "i couldn't write that app/feature because javascript doesn't have classes".
I agree.
So far, we're discussing the problem from the JS writers perspective. But I am more concerned by the consequences of how adding "classes" will be received by newcomers who come from Java or PHP. Will this syntax help them to write JS or encourage them to not learn JS fundamentals? I only have the question, not the answer, unfortunately. I read a lot of JS code and it's sometimes depressing to see people writing "new Array()" all over the place and use their "arrays" as a JS writer would normally use objects. Such code is very likely to be written by people who come from the PHP world where "array" is the easiest (string) key -> value language construct. I agree that this
example is harmless, but I am worried that there could be other issues when people don't learn JS fundamentals.
Moreover, I've been really interested by this presentation [1] where one point is that some OO programming languages have two OO concepts (Object and Class) while some others (including JS) only have one (Object). I'm worried that adding a class abstraction may lead us to the same issues noted in the presentation.
David
On May 23, 2011, at 11:25 AM, Bob Nystrom wrote:
One thing I'd like the proposal to support, which it doesn't currently, is initializers on instance property declarations. Then you could do:
class C { public _list = []; }
With that, you'll correctly get a new _list on each instance of C when it's created.
But (we've argued, I forget where so repeating it here), this looks like [] is evaluated once when the class declaration is evaluated. That is not what you intend.
Then at some point (in the last thread on this) I remembered parameter default values, but they cover only missing parameters to the constructor. This _list member could be private. But it has to be initialized in a body that executes once per instantiation, which is not the class body -- it's the constructor body.
On May 23, 2011, at 11:33 AM, Luke Hoban wrote:
there are lots of ES.next features that let us do something that we could not do at all previously (weak tables and refs are a good example). features that enable new kinds of applications we couldn't previous build.
+1 on this point. There’s been a lot of discussion of syntactic sugar recently, but much less discussion of the features that provide new capabilities, or new opportunities for meaningful performance enhancements that make new kinds of applications possible. Features like private names as a library feature, String and Math enhancements, proxies, runtime capabilities for module loading, typed arrays, and others are incremental enabling value which unblocks or makes realistic things which aren’t currently possible. These are also features that are more likely to be implemented sooner, and which developers can adopt more quickly and smoothly via feature-detection in 'text/javascript'.
We have almost all of these already on the wiki and most of them in for ES.next, or else on the agenda this week to get in. I write "almost" because it seems to me you've mentioned two things, one that is not enough, the other that doesn't work:
-
Runtime module loading from old code means new code in strings or loaded via XHR. First, we have Module Loaders, which can do that. Second, it is not enough to disparage modules in full including new syntax. So it should not be used to undermine new module syntax.
-
Private name APIs usable from application/javascript without opt into Harmony would not work correctly on old browsers. That seems like a fatal flaw.
Is there anything else (other than starting this thread) I can do to make committee consider Function.prototype.extend
as an alternative to a proposed class sugar ?
Thanks
Irakli Gozalishvili Web: www.jeditoolkit.com Address: 29 Rue Saint-Georges, 75009 Paris, France (goo.gl/maps/3CHu)
On Jun 12, 2011, at 2:22 AM, Irakli Gozalishvili wrote:
Hi,
Is there anything else (other than starting this thread) I can do to make committee consider
Function.prototype.extend
as an alternative to a proposed class sugar ?
Could you show Function.prototype.extend again, and say how it solves the super-construct and super-method-call problems?
On 13.06.2011 1:18, Brendan Eich wrote:
On Jun 12, 2011, at 2:22 AM, Irakli Gozalishvili wrote:
Hi,
Is there anything else (other than starting this thread) I can do to make committee consider
Function.prototype.extend
as an alternative to a proposed class sugar ?Could you show Function.prototype.extend again, and say how it solves the super-construct and super-method-call problems?
If interested, I know at least three versions of normal super
sugar in
ES3 code:
-
using wrappers for every descendant method with the same name (the technique is to (a) set
this.super
to parent, (b) activate parent method and get result, (c) return result to child) -
using
Object.prototype
to storesuper
-
using
arguments.caller
(banned in ES5-strict, non-standard in ES3, but I normally used it in my projects)
If will be needed, I can write the code for all three techniques (the later though can be found here: DmitrySoshnikov/def.js/blob/master/def.js#L80
That is, it's not actually the main issue, we normally can write in ES3/5 code something like this:
var Foo = Class({ constructor: function (a) { this.a = a; }, activate: function () { return this.a; } });
var Bar = Class({ constructor: function (a, b) { this.super(a); this.b = b; }, activate: function () { console.log(this.b + this.super()); } });
var bar = new Bar(10, 20); bar.activate(); // 30!
P.S.: of course this.super() is much more convenient than (real code
from e.g. ExtJS) Bar.superclass.constructor.apply(this, arguments)
(what? are you kidding me, guys? Seems you just like syntactic noise).
But unfortunately because of hack-nature (caller
is not-standard and
banned, people afraid to augment Object.prototype
, wrappers are not so
efficient), so it's not e.g. for cross-browser code. Though, I was
pleased to use it in my library in the project where I had
SpiderMonkey-only stuff (patched Thunderbird), so there I used that
this.super
actively (OK, actually this._super
, because only since
ES5 we can use keywords as properties).
However, regardless that we can implement convinient and sugared super calls as a library, classes from the box will be more convenient IMO.
Dmitry.
On 13.06.2011 1:43, Dmitry A. Soshnikov wrote:
On 13.06.2011 1:18, Brendan Eich wrote:
On Jun 12, 2011, at 2:22 AM, Irakli Gozalishvili wrote:
Hi,
Is there anything else (other than starting this thread) I can do to make committee consider
Function.prototype.extend
as an alternative to a proposed class sugar ?Could you show Function.prototype.extend again, and say how it solves the super-construct and super-method-call problems?
If interested, I know at least three versions of normal
super
sugar in ES3 code:
using wrappers for every descendant method with the same name (the technique is to (a) set
this.super
to parent, (b) activate parent method and get result, (c) return result to child)using
Object.prototype
to storesuper
using
arguments.caller
(banned in ES5-strict, non-standard in ES3, but I normally used it in my projects)If will be needed, I can write the code for all three techniques (the later though can be found here: DmitrySoshnikov/def.js/blob/master/def.js#L80
Forgot to mention example:
def ("Person") ({ init: function (name) { this.name = name; },
speak: function (text) { alert(text || "Hi, my name is " + this.name); } });
def ("Ninja") << Person ({ init: function (name) { this.base(name); },
kick: function () { this.speak("I kick u!"); } });
var ninjy = new Ninja("JDD");
ninjy.speak(); ninjy.kick();
Dmitry.
On Sunday, 2011-06-12 at 23:18 , Brendan Eich wrote:
On Jun 12, 2011, at 2:22 AM, Irakli Gozalishvili wrote:
Hi,
Is there anything else (other than starting this thread) I can do to make committee consider
Function.prototype.extend
as an alternative to a proposed class sugar ?Could you show Function.prototype.extend again,
Here is gist I wrote before: gist.github.com/986487#file_implementation.js
and say how it solves the super-construct and super-method-call problems?
I don't have any (in js implementable solution) for those problems, also I think sugar for super
can be a separate thing. Gist contains example with super that behave exactly the same as in harmony proposal for classes.
On Jun 12, 2011, at 2:52 PM, Irakli Gozalishvili wrote:
Here is gist I wrote before:
What Function.create are you using there?
Is there a missing return statement in function extend?
and say how it solves the super-construct and super-method-call problems?
I don't have any (in js implementable solution) for those problems, also I think sugar for
super
can be a separate thing. Gist contains example with super that behave exactly the same as in harmony proposal for classes.
super.update();
// Desugars to:
// Object.getPrototypeOf(Object.getPrototypeOf(this)).update.call(this);
That comment is wrong, or worse: it implies the wrong spec. This function code does not want to depend on |this|, which could be rebound. You want to depend on the [[Prototype]] of the enclosing object, or if contained in class C syntax at the right level (not nested in arbitrary function expressions or inner function definitions), C.prototype.[[Prototype]].
Allen worked through this idea already:
On Monday, 2011-06-13 at 24:03 , Brendan Eich wrote:
On Jun 12, 2011, at 2:52 PM, Irakli Gozalishvili wrote:
Here is gist I wrote before:
What Function.create are you using there?
Is there a missing return statement in function extend?
Yeap, sorry! It's there now.
and say how it solves the super-construct and super-method-call problems?
I don't have any (in js implementable solution) for those problems, also I think sugar for
super
can be a separate thing. Gist contains example with super that behave exactly the same as in harmony proposal for classes.super.update(); // Desugars to: // Object.getPrototypeOf(Object.getPrototypeOf(this)).update.call(this);
That comment is wrong, or worse: it implies the wrong spec. This function code does not want to depend on |this|, which could be rebound. You want to depend on the [[Prototype]] of the enclosing object, or if contained in class C syntax at the right level (not nested in arbitrary function expressions or inner function definitions), C.prototype.[[Prototype]].
Allen worked through this idea already:
Could be, I have not spend time thinking on super as I considered as separate issue. I'll take a look at Allen's work thanks for the link.
Another idea I had was that super can be similar to private. Here is some example:
function super(self) { var proto = Object.getPrototypeOf(Object.getProtypeOf(self)); Proxy.create({ ... get: function (receiver, name) { // Shorter to constructor would be nice: super(this).new(options) if (name === 'new') name = 'constructor' // make sure to apply for getter / setters / and methods only. // super(this).method(a, b) return proto[name].bind(self); } ... }, proto); }
super(this).new(options); super(this).constructor(a, b); super(this).method(a, b);
Obviously I don't suggest to use proxy, I just used to illustrate idea.
Also please not that constructor will be enough as users will be able to implement getter new
returning this.constructor
on a base class for shorter syntax.
On Jun 12, 2011, at 3:18 PM, Irakli Gozalishvili wrote:
On Monday, 2011-06-13 at 24:03 , Brendan Eich wrote:
On Jun 12, 2011, at 2:52 PM, Irakli Gozalishvili wrote:
Here is gist I wrote before:
What Function.create are you using there?
Is there a missing return statement in function extend?
Yeap, sorry! It's there now.
Ok. But what is that Function.create your gist relies on?
Another idea I had was that super can be similar to private. Here is some example:
Please note that the private(this)/private(other) syntax is intentional straw, to be burned up and replaced with something better.
I think you're trying to self-host too much. People can write classes the hard way, with super too. They need sugar, not salt or pepper ;-).
On Monday, June 13, 2011, Brendan Eich <brendan at mozilla.com> wrote:
On Jun 12, 2011, at 3:18 PM, Irakli Gozalishvili wrote:On Monday, 2011-06-13 at 24:03 , Brendan Eich wrote:
On Jun 12, 2011, at 2:52 PM, Irakli Gozalishvili wrote: Here is gist I wrote before:https://gist.github.com/986487#file_implementation.js
What Function.create are you using there? Is there a missing return statement in function extend? Yeap, sorry! It's there now. Ok. But what is that Function.create your gist relies on?
It was proposed before I believe, it's like Object.create
Function.create = function (proto, func) { var f = function() { return func.apply(this, arguments); } f.proto = proto return f }
Another idea I had was that super can be similar to private. Here is some example: Please note that the private(this)/private(other) syntax is intentional straw, to be burned up and replaced with something better. I think you're trying to self-host too much. People can write classes the hard way, with super too. They need sugar, not salt or pepper ;-). /be
I just don't know how wild I can go with idea :) Isn't it better to leave off super for the moment and tackle that separately ?
On Jun 12, 2011, at 3:38 PM, Irakli Gozalishvili wrote:
On Monday, June 13, 2011, Brendan Eich <brendan at mozilla.com> wrote:
On Jun 12, 2011, at 3:18 PM, Irakli Gozalishvili wrote:On Monday, 2011-06-13 at 24:03 , Brendan Eich wrote:
On Jun 12, 2011, at 2:52 PM, Irakli Gozalishvili wrote: Here is gist I wrote before:https://gist.github.com/986487#file_implementation.js
What Function.create are you using there? Is there a missing return statement in function extend? Yeap, sorry! It's there now. Ok. But what is that Function.create your gist relies on?
It was proposed before I believe, it's like Object.create
Function.create = function (proto, func) { var f = function() { return func.apply(this, arguments); } f.proto = proto return f }
No, that was too proxy-like. See
strawman:name_property_of_functions
Starting at "The major objection to losing ...."
Another idea I had was that super can be similar to private. Here is some example: Please note that the private(this)/private(other) syntax is intentional straw, to be burned up and replaced with something better. I think you're trying to self-host too much. People can write classes the hard way, with super too. They need sugar, not salt or pepper ;-). /be
I just don't know how wild I can go with idea :) Isn't it better to leave off super for the moment and tackle that separately ?
Not obviously. super outside of classes does not necessarily pay for itself (I did not remember it going to Harmony at the last meeting). And super inside of class should pay off as the classes proposal hopes -- 'super' in that context has no open issues.
Ok I wrote up another gist
gist.github.com/7e649e8c33d412e90178
that no longer refers to any non-existing functions like Function.create
and addresses super
sugar issue using base
function (In a similar way as in Dmitry's link).
Also, implementation uses deprecated caller
, non-standard __proto__
, pseudo internal properties [[target]]', '[[name]]', '[[this]]' and function wrappers, but I hope / assume native implementation will be able to achieve same results without any of those.
One disadvantage in comparison to super
is that you can no longer call overlaid X methods from Y method, from my experience thats pretty rare case and can easily be workarounded. Alternatively base can have be improved using proxies to support base[X](a, b) case as well.
-- Irakli Gozalishvili Web: www.jeditoolkit.com Address: 29 Rue Saint-Georges, 75009 Paris, France (goo.gl/maps/3CHu)
I think there lot's of proposals for ES.next that require syntax extensions, which is probably worth if new functionality added or shortens most commonly used constructs like functions (were no other option is available). In case of this proposal: strawman:classes_with_trait_composition#open_issues even though I like it I'm not sure adding new syntax is worth it.
I'm not suggesting that sugar for class composition is not necessary, example from three.js used by proposal highlights necessity pretty very well, I'm just thinking of doing that without introducing new syntax, here is one option: gist.github.com/986487
This way syntax noise may be reduced in addition this can be shimmed into current JS by implementing
Function.prototype.extend
.Also every single frameworks today does something similar in one form or another, IMO all is necessary is to have a standard that will let bikeshedding go away. I think there is also a good precedent of this with
Function.prototype.bind
.Here are some related links:
documentcloud.github.com/backbone, base2.googlecode.com/svn/version/1.0.2/doc/base2.html#/doc/!base2.Base, prototypejs.org/learn/class-inheritance, startdojo.com/2011/03/02/dojo-classes-inherited-and-constructors, mootools.net/docs/core/Class/Class, ejohn.org/blog/simple-javascript-inheritance
Kind
Irakli Gozalishvili Web: www.jeditoolkit.com Address: 29 Rue Saint-Georges, 75009 Paris, France