Re: Good-bye constructor functions?
Sorry if I was too dense in my reply so things weren't understood. I don't know how to reply put things efficiently because without big picture, isolated cherry-picks seem ridiculous and properly put big picture is often tl;dr-ed. Concrete replies below. Allen Wirfs-Brock wrote: On Jan 8, 2013, at 12:45 AM, Herby Vojčík wrote: Allen Wirfs-Brock wrote: On Jan 7, 2013, at 4:09 PM, Herby Vojčík wrote: ... I just don't see such an inconsistency in the current ES6 spec. draft. A constructor is just a function that is usually called with a this value. This is true whether it is called by the [[Constructor]] internal method or via a super/super.constructor call. In either case, the primary purpose of the constructor is to perform initialization actions upon the this value. Where is the inconsistency? (I claim that) in any circumstances, what developers want to express when writing `super(...args)` in the constructoro of SubFoo, is: On my this, which is now instance of SubFoo, I want the identical initialization code to be run, which `new Foo(...args)` would run to initialize newly created instance of Foo That's not true. Because the spec is trying to serve two masters: F and F.prototype.constructor. It is impossible. The fixed semantics of [[Construct]] for `class` ((1) above) is fixing this by only serving one master: F.prototype.constructor (in line 3). I agree with your above statement about initialization. But I also content that is exactly what the current specification of super does within a constructor function (subject, of course to what the invoked methods actually are coded to do). What I don't see is why you What's it? super does call the .prototype.constructor of superclass, yes, I know that well, ... think otherwise. I need a clearer concrete explanation of what you see is the problem, prefably without a forward reference to what you think is the solution. ...but new does not call the .prototype.constructor. There this does not hold for `super(...args)` behaviour: On my this, which is now instance of SubFoo, I want the identical initialization code to be run, which `new Foo(...args)` would run to initialize newly created instance of Foo. And if you argue they are identical at the beginning, I say they can be desynchronized, they will, and it does not matter how is the default case. This state of serving two masters (new F, super F.prototype.constructor) is a design issue / inconsistency / bug in the core of the language. And sorry if I mention the aolution (it is simply call .prototype.constructor in new for `class`), but it saves the model of super-without-special-cases for constructor, which is fine (special cases aren't). The only anomaly I see is that a handful of legacy built-ins do completely different things for new operator originated calls in contrast to regular function calls. There is no confusion in the spec. about this and the mechanism for accomplishing it. However such split behavior can't be declaratively defined in a normal function or class declaration. In the other thread at https://mail.mozilla.org/pipermail/es-discuss/2013-January/027864.html I described how this split behavior an be procedurally described in such declarations and also described how the same technique can be applied to the offending built-in constructors (r any user defined class constructor) to discriminate between initialization and called behavior, even when called via super. Yes, but it is a workaround. Or, alternatively stated, it shows how the objective can be met without any further complicating the actual language. That is arguably a good thing, not just a woprkaround Taken ad absurdum, JS is turing complete, you can achieve anything without complicating the actual language. The only fix I saw that you made to super was that in you sketch constructor methods defined via a class definition are only used for instance initialization, so there doesn't need to be any code to determine between initialization and call behavior. Constructor behavior is always initialization behavior. Yes, it is there. As a free bonus, btw. The main druver for the design was new/super two masters fixing. Then, later (Brendan Eich will not like this ;-) ) the beauty of breaking the tight coupling and not needing [[Call]] at all for object with [[Construct]]. There are in fact TWO freebies: - [[Call]] separated from [[Construct]] - you get default constructor for free; by not defining it explicitly. Sorry, I still don't see it, you have to explain both the problem and your solution more concretely. You probably see [[Call]] / [[Construct]] separation - class object's [[Call]] is orthogonal to what [[Construct]] does, since it is specified to call .prototype.constructor. It follows naturally from the fact that class !== constructor. So the issue needing explanation is free default constructor, I presume (tell me if I am wrong). It goes this way: if the [[Construct]] has proposed
Re: Good-bye constructor functions?
Allen Wirfs-Brock wrote: On Jan 7, 2013, at 4:09 PM, Herby Vojčík wrote: Allen Wirfs-Brock wrote: OK, I ready you proposal. I'll summaries it: class Sub extends Super { constructor (...args) { super(...args); //or super.constructor(...args) because they mean the same thing. } } creates an (almost) ordinary object that is the initial value of the Sub binding. The [[Prototype]] of Sub is the value of Super. Sub is created with a prototype property whose value is a new ordinary object whose [[Prototype]] values is Super.prototype. Sub.prototype has a method property named constructor whose value is a super bound method function with the user specified body. There is nothing special about this method, it it just like any other method defined in a class declaration. In particular it does not have a [[Construct]] internal property. The only thing exotic about Sub is that the object has a [[Construct]] internal method. It does not have a [[Call]] internal method. The definition of its [[Construct]] in almost JS pseudo code is: (1): //internal method this.[[Constructor]](argList): let newObj = this.@@create(); //eg Sub.@@create(); let replacementObj = this.prototype.constructor.apply(newObj, argList); if (typeof replacementObj == object replacementObj !== null) return replacementObj return newObj; (end of (1)) Implications: Sub === Sub.prototype.constructor evaluates to false. To create a new instance of Sub say: new Sub() The following is a TypeError, because Sub does not have a [[Call]] internal method: Sub() The following is a TypeError, because Sub.prototype.constructor does not have a [[Construct]] internal method: new Sub.prototype.constructor() Sub.prototype.constructor can be called directly or as a method invocation including via a super call a subclass of Sub. It seems to me, all you have accomplished here is to make it illegal to call a class object directly as a function. If we were starting From technical PoV*, yes. Oh, and I fixed the super / new inconsistency. I just don't see such an inconsistency in the current ES6 spec. draft. A constructor is just a function that is usually called with a this value. This is true whether it is called by the [[Constructor]] internal method or via a super/super.constructor call. In either case, the primary purpose of the constructor is to perform initialization actions upon the this value. Where is the inconsistency? (I claim that) in any circumstances, what developers want to express when writing `super(...args)` in the constructoro of SubFoo, is: On my this, which is now instance of SubFoo, I want the identical initialization code to be run, which `new Foo(...args)` would run to initialize newly created instance of Foo That's not true. Because the spec is trying to serve two masters: F and F.prototype.constructor. It is impossible. The fixed semantics of [[Construct]] for `class` ((1) above) is fixing this by only serving one master: F.prototype.constructor (in line 3). The only anomaly I see is that a handful of legacy built-ins do completely different things for new operator originated calls in contrast to regular function calls. There is no confusion in the spec. about this and the mechanism for accomplishing it. However such split behavior can't be declaratively defined in a normal function or class declaration. In the other thread at https://mail.mozilla.org/pipermail/es-discuss/2013-January/027864.html I described how this split behavior an be procedurally described in such declarations and also described how the same technique can be applied to the offending built-in constructors (r any user defined class constructor) to discriminate between initialization and called behavior, even when called via super. Yes, but it is a workaround. Bonding identity of class with identity of constructor is not a law. It was just a workaround to make class without `class`. Nor, ultimately, can it be said it is preferred by the crowd (they have little other oossibility, it is not a preference but a legacy; similar to assigment is user preference over Object.define was not true, because of ES3 legacy and ES5 being fairly new; I think this is the same pattern). Physical separation allows it naturally, should one want it. By stopping serving F and serving F.prototype.constructor exclusively in new/super. The only fix I saw that you made to super was that in you sketch constructor methods defined via a class definition are only used for instance initialization, so there doesn't need to be any code to determine between initialization and call behavior. Constructor behavior is always initialization behavior. Yes, it is there. As a free bonus, btw. The main druver for the design was new/super two masters fixing. Then, later (Brendan Eich will not like this ;-) ) the beauty of breaking the tight coupling and not needing [[Call]] at all for object with [[Construct]]. There are in fact TWO freebies: - [[Call]] separated
Re: Good-bye constructor functions?
Kevin Smith wrote: I think it's arguable either way. I mean, the initializer-only behavior advocated by Allen et al makes the class useless when called (as opposed to new'd). I don't think such an opinionated approach is really necessary. One can account for the funky behavior of the built-ins by providing an additional call hook on the class, and leave everything else as is. When the class is called (as opposed to new'd), it would use that hook instead of the constructor. (super() would still use the parent's constructor function.) It could be supported with syntax like so: class C { static(...args) { /* call behavior */ } } Read my reply to Allen, it shows more general solution there. In a few words, with class and constructor separated, you can specify what object should represent the class (and is given appropriate .prototype and [[Construct]]), would you want to (otherwise, plain object is created for you). One of the options is you can specify that a function should represent it: class C { static function(...args) { /* call behaviour */ }; } though you can of course put any expression there. Another good candudate is {...} object literal. I think one could also make a case that the default behavior for such a hook would be to new the class, as some built-ins do (and as many current JS classes attempt to do): class C { static(...args) { return new C(...args); } } Yeah, in my proposal there is no default [[Call]] (have no reason for general object). But it is more general. This seems like a pretty clean design to me. { Kevin } Herby ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Good-bye constructor functions?
Herby Vojčík wrote: In a few words, with class and constructor separated, you can specify what object should represent the class Why? Where are the use-cases driving this, which goes beyond class as constructor function that can be called as well as new'ed? (and is given appropriate .prototype and [[Construct]]), By mutation of the result of an evaluated expression? would you want to (otherwise, plain object is created for you). What use-case? One of the options is you can specify that a function should represent it: class C { static function(...args) { /* call behaviour */ }; } though you can of course put any expression there. Another good candudate is {...} object literal. This is innovation beyond any cowpath or stable strawman we've promoted to Harmony in time for ES6. It goes beyond what we need, which Kevin pointed out succinctly: per-class @@call specialization. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Good-bye constructor functions?
Brendan Eich wrote: Herby Vojčík wrote: In a few words, with class and constructor separated, you can specify what object should represent the class Why? Where are the use-cases driving this, which goes beyond class as constructor function that can be called as well as new'ed? This question is unfair: no one have use cases for this, when it was not possible at all in the language. I'd ask the contrary*: what are the use case of tying class with its own constructor function? *given the virtual scenario where those two are not tied (and is given appropriate .prototype and [[Construct]]), By mutation of the result of an evaluated expression? Good question. Well, that static Expr; was the second step, a possible cowpath when you allow the cows visit those green pastures beyond class tied to the constructor. So don't take it (the static syntax) as true proposal. But to answer, yes, by modifying it. In case of {} literal object, it's fine; no problem. In case of function (...) {...} (or (...) = {}) also no problem: it is fresh creation and its [[Construct]] and .prototype may be changed accordingly. In case of any other expression: you've been warned, but if you really want, you can (in case you put already existing class-like there, it fails, already having non-writable .prototype). would you want to (otherwise, plain object is created for you). What use-case? Green pastures. One of the options is you can specify that a function should represent it: class C { static function(...args) { /* call behaviour */ }; } though you can of course put any expression there. Another good candudate is {...} object literal. This is innovation beyond any cowpath or stable strawman we've promoted to Harmony in time for ES6. It goes beyond what we need, which Kevin pointed out succinctly: per-class @@call specialization. This static syntax was an example of what can follow. It was kind-of a carrot. Showing that you can do this separation very cleanly, if you have the class and constructor separated. What I care for now, is the first step - opening the gates for [[Construct]] and decoupling it from need of [[Call]]able function. Or at least the first half-step: the fixed [[Construct]] semantics, that fixes super doesn't run the same initialization code as new SuperClass would, and paving the way to, possibly, [[Construct]] on different objects. /be Herby ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Good-bye constructor functions?
Herby Vojčík wrote: Brendan Eich wrote: Herby Vojčík wrote: In a few words, with class and constructor separated, you can specify what object should represent the class Why? Where are the use-cases driving this, which goes beyond class as constructor function that can be called as well as new'ed? This question is unfair: no one have use cases for this, when it was not possible at all in the language. Nothing's unfair about designing by use-cases, and that's how a lot of the Web standards roll. Notorious counter-examples have gone off the rails by doing otherwise, but I won't try to sum of the problems so simplistically. Nevertheless, we need use-cases, preferably ones already approximated by library code or transpilers. I'd ask the contrary*: what are the use case of tying class with its own constructor function? Designing means saying no and leaving things out (N. Wirth, IIRC). That's point #1. JS already has the prototypal pattern with constructor function as class, and that's what Harmony (now ES6) classes sugar. Point #2. Stopping here. See my other reply on path dependency if you haven't yet. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Good-bye constructor functions?
On Jan 8, 2013, at 12:45 AM, Herby Vojčík wrote: Allen Wirfs-Brock wrote: On Jan 7, 2013, at 4:09 PM, Herby Vojčík wrote: ... I just don't see such an inconsistency in the current ES6 spec. draft. A constructor is just a function that is usually called with a this value. This is true whether it is called by the [[Constructor]] internal method or via a super/super.constructor call. In either case, the primary purpose of the constructor is to perform initialization actions upon the this value. Where is the inconsistency? (I claim that) in any circumstances, what developers want to express when writing `super(...args)` in the constructoro of SubFoo, is: On my this, which is now instance of SubFoo, I want the identical initialization code to be run, which `new Foo(...args)` would run to initialize newly created instance of Foo That's not true. Because the spec is trying to serve two masters: F and F.prototype.constructor. It is impossible. The fixed semantics of [[Construct]] for `class` ((1) above) is fixing this by only serving one master: F.prototype.constructor (in line 3). I agree with your above statement about initialization. But I also content that is exactly what the current specification of super does within a constructor function (subject, of course to what the invoked methods actually are coded to do). What I don't see is why you think otherwise. I need a clearer concrete explanation of what you see is the problem, prefably without a forward reference to what you think is the solution. The only anomaly I see is that a handful of legacy built-ins do completely different things for new operator originated calls in contrast to regular function calls. There is no confusion in the spec. about this and the mechanism for accomplishing it. However such split behavior can't be declaratively defined in a normal function or class declaration. In the other thread at https://mail.mozilla.org/pipermail/es-discuss/2013-January/027864.html I described how this split behavior an be procedurally described in such declarations and also described how the same technique can be applied to the offending built-in constructors (r any user defined class constructor) to discriminate between initialization and called behavior, even when called via super. Yes, but it is a workaround. Or, alternatively stated, it shows how the objective can be met without any further complicating the actual language. That is arguably a good thing, not just a woprkaround ... The only fix I saw that you made to super was that in you sketch constructor methods defined via a class definition are only used for instance initialization, so there doesn't need to be any code to determine between initialization and call behavior. Constructor behavior is always initialization behavior. Yes, it is there. As a free bonus, btw. The main druver for the design was new/super two masters fixing. Then, later (Brendan Eich will not like this ;-) ) the beauty of breaking the tight coupling and not needing [[Call]] at all for object with [[Construct]]. There are in fact TWO freebies: - [[Call]] separated from [[Construct]] - you get default constructor for free; by not defining it explicitly. Sorry, I still don't see it, you have to explain both the problem and your solution more concretely. When such freebies appear, for me this is the signal that someth8ing clicks, eg., the design matches requirements very good. However, you don't support non-new invocation behavior, at all, on class objects so you can't use a class to self-host an implementation of, for example, RegExp. You also, don't do any thing to support correct Read the rest of the reply, please. The exact contrary is in fact true. I do. (meta question: why it is so often here that people are cutting the rest and reply only on the first line dismissing the rest?) The proposal was first step: do class as plain objrct with [[Construct]], thereby blessing class/constructor separation. The second step is: when plain object can do, anything can do (the only question is how to specify it). It was sketched with more examples in the rest. I did read the rest. I don't see any provisions for calling the class object. Can you point me concretely to where you define it? Allen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: Good-bye constructor functions?
Nothing's unfair about designing by use-cases, and that's how a lot of the Web standards roll. I'm not sure I'm on topic because I didn't go throug the full thread, only a few mail, but what about the case of non-constructible DOM objects? Until recently (still now on IE) you could only create a custom event via document.createEvent(CustomEvent) and the CustomEvent interface had no constructor (while CustomEvent.prototype do work and is the prototype of a custom event). In IE10, document.createEvent(CustomEvent).constructor==CustomEvent but CustomEvent is not a function, it's just a holder for CustomEvent.prototype==Object.getPrototypeOf(customEv). Best regards, François ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Good-bye constructor functions?
On Jan 7, 2013, at 9:30 PM, Kevin Smith wrote: I think it's arguable either way. I mean, the initializer-only behavior advocated by Allen et al makes the class useless when called (as opposed to new'd). Not really. If you want to, you can still do the same sort of ad hoc new/call discrimination in a class constructor that some JS programmers have historically done in constructor functions: class Foo { constructor() { if (this===undefined) return new Foo(); this.isFoo = true } } If you like this pattern you can still do it, plus I showed that it can still work with subclassing (including of built-ins) and super constructor calls. Whether this is a good or bad (or neutral) pattern is a separable issue. Also, we don't know which patterns will become widely adopted by JS programmers as class declaration come into wide use. No doubt, at first, they will just follow either the JS patterns they already know or patterns they learned in other languages. But in the long rung specific JS class declaration best practices will emerge. None of us know exactly what they will be. I don't think such an opinionated approach is really necessary. One can account for the funky behavior of the built-ins by providing an additional call hook on the class, and leave everything else as is. When the class is called (as opposed to new'd), it would use that hook instead of the constructor. (super() would still use the parent's constructor function.) I think any stylistic opinions are secondary. The key point is that we can account for the funky behavior of built-ins and support their subclassing without anything in addition to what we already have in the ES6 spec. Maybe in the future we may decide to add language to support this pattern but it isn't essential that it happens now. It could be supported with syntax like so: class C { static(...args) { /* call behavior */ } } I think one could also make a case that the default behavior for such a hook would be to new the class, as some built-ins do (and as many current JS classes attempt to do): class C { static(...args) { return new C(...args); } } This seems like a pretty clean design to me. We haven't yet found consensus on any class syntax extensions beyond the basic max-min class syntax. I suspect that it would be hard to get buy-in on the above syntax. But let's look at how we could do it without new syntax and also examine some of the implications. We could accomplish pretty much the same thing via a @@call method on a class property. To make this work we would have to make class constructors a new kind of exotic object (they currently are specified as ordinary function objects). Their [[Call]] internal method could be defined approximately as the following JS pseudo code: internal method [[Call]] (thisValue, argList) { var ctor = this; //the constructor object this internal method was called upon var calledHandler = ctor.@@call; if (calledHandler !== undefined) { if (isFunction(calledHandler)) return calledHandler.[[Call]](thisValue,argList) } return the result of applying the ordinary function [[Call]] internal method to ctor with arguments thisValue and argList } So, C() woud invoke the C.@@call method, if it was defined or inherited. These objects would also need to have a different [[Construct]] internal method so thatnew C() would invoke C using the body provide by the constructor body in the class declaration rather than going through the revised [[Call]] internal method.. That gives us the split dispatch for new/call. so now let's think about what happens with super calls during evaluation of both the constructor body and a @@call method. First, I want to clarify that currently expressions of the form super() have no special semantics within constructor bodies. In any concise method, super() is just shorthand for super.methodName() where methodName is the property key used in the concise method definition. Now consider: import call_symbol ...; class B { constructor() {console.log(B constructor)} } Object.mixin(B, { [call_symbol] () {console.log(B @@call method)} }); class C { constructor() { super(); //or super.constructor() console.log(C constructor); } } So, using the current specification of super() and the new [[Call semantics above, this expresion: new C() will log: B @@call method, C constructor. How do we fix this? Presumably by giving special treatment to super() and super.constructor() (but only within concise constructor methods?? What should a super.constructor() call mean in other methods?). What would that special treatment be? It can't just be a [[Construct]] call to the result of the super-lookup of constructor because that would allocated a new object, and use it as the this value which is not the
Re: Good-bye constructor functions?
Allen, Thanks for the analysis! I think as long as the current design isn't future-hostile with regard to this @@call hook, we're good. Really, declarative class-side methods would be a *much* bigger win than providing support for the call/new split. { Kevin } ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Good-bye constructor functions?
Allen Wirfs-Brock wrote: Even if we think we should discourage direct calls to class objects (I think I'm now in that camp) (Why so?) /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Good-bye constructor functions?
I also agree with the sentiment. Splitting allocation from initialization helps to clarify the two separate roles that constructors have traditionally filled, and how a class could/should diverge from that. The class itself is the thing that should allocate the new object, the constructor initializes newly minted objects. Perhaps a middle ground with the backward compatibility issue that awb mentions would be that calling a class is always treated as constructing it, in that it always allocates a new object if it's not receiving a newly created one from a subclass that's in the process of initializing. On Mon, Jan 7, 2013 at 6:24 PM, Brendan Eich bren...@mozilla.com wrote: Allen Wirfs-Brock wrote: Even if we think we should discourage direct calls to class objects (I think I'm now in that camp) (Why so?) /be __**_ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/**listinfo/es-discusshttps://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Good-bye constructor functions?
Although I already see flaws with that idea, since I just recently made use of Constructor.call(existingInstance) to reinitialize an instance to defaults. So nevermind that idea... On Mon, Jan 7, 2013 at 6:31 PM, Brandon Benvie bran...@brandonbenvie.comwrote: I also agree with the sentiment. Splitting allocation from initialization helps to clarify the two separate roles that constructors have traditionally filled, and how a class could/should diverge from that. The class itself is the thing that should allocate the new object, the constructor initializes newly minted objects. Perhaps a middle ground with the backward compatibility issue that awb mentions would be that calling a class is always treated as constructing it, in that it always allocates a new object if it's not receiving a newly created one from a subclass that's in the process of initializing. On Mon, Jan 7, 2013 at 6:24 PM, Brendan Eich bren...@mozilla.com wrote: Allen Wirfs-Brock wrote: Even if we think we should discourage direct calls to class objects (I think I'm now in that camp) (Why so?) /be __**_ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/**listinfo/es-discusshttps://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Good-bye constructor functions?
Allen Wirfs-Brock wrote: OK, I ready you proposal. I'll summaries it: class Sub extends Super { constructor (...args) { super(...args); //or super.constructor(...args) because they mean the same thing. } } creates an (almost) ordinary object that is the initial value of the Sub binding. The [[Prototype]] of Sub is the value of Super. Sub is created with a prototype property whose value is a new ordinary object whose [[Prototype]] values is Super.prototype. Sub.prototype has a method property named constructor whose value is a super bound method function with the user specified body. There is nothing special about this method, it it just like any other method defined in a class declaration. In particular it does not have a [[Construct]] internal property. The only thing exotic about Sub is that the object has a [[Construct]] internal method. It does not have a [[Call]] internal method. The definition of its [[Construct]] in almost JS pseudo code is: //internal method this.[[Constructor]](argList): let newObj = this.@@create(); //eg Sub.@@create(); let replacementObj = this.prototype.constructor.apply(newObj, argList); if (typeof replacementObj == object replacementObj !== null) return replacementObj return newObj; Implications: Sub === Sub.prototype.constructor evaluates to false. To create a new instance of Sub say: new Sub() The following is a TypeError, because Sub does not have a [[Call]] internal method: Sub() The following is a TypeError, because Sub.prototype.constructor does not have a [[Construct]] internal method: new Sub.prototype.constructor() Sub.prototype.constructor can be called directly or as a method invocation including via a super call a subclass of Sub. It seems to me, all you have accomplished here is to make it illegal to call a class object directly as a function. If we were starting From technical PoV*, yes. Oh, and I fixed the super / new inconsistency. over that might be a reasonable design choice. But we have a legacy to consider. Even if we think we should discourage direct calls to Ignoring Foo.call(this) replacable by super, mostly, class is used for new Foo and for Foo.staticVar. I argued the change is big in its wider implication, even scary, maybe (but I see it as in fact minimal), but should not (imo) beat legacy much; because the fact of the tight coupling was not exploited so much. class objects (I think I'm now in that camp) actually making it an error seems like it may be too big of a step away from the legacy conventions. And there is no better time to do that step than now, when class is new construct in the language. *There is more than narrow technical PoV. By returning plain object with exotic [[Construct]], working in nearly every detail as a class, while affording not to be the constructor itself, you effective say openly You can use any [[Construct]]ified object as a class in ES6+. Gates are finally open. It can be blessed by Reflect.makeClass(classObject, protoObject) or similar API. It can bring lots of new patterns and cowpaths into the language. If people don't want plain object to be the class, but their existing object x, they do Reflect.makeClass(x, (class {...}).prototype); and they then can `new x`. They choose what they will use as x for new operator; because now class is one object and constructor is another. For example, the criticized pattern of 'function that does differently for [[Call]] and [[Construct]]' could now be created as well. Just 'makeClass' it. The main message of this proposal is that beyond fixing super/new inconsistency, it opens new world for classes, not restricted by the legacy tightly bound class===constructor objects. And by making class to return this kind of 'decoupled' classes, this widened view to who may be used as a class in new / extends? is effectively blessed. You cannot open it later. Because there will be lagacy code that already uses `class`. (I'd stress again, that, imo, that change is very little, too little for the fruits it can bear. Fix me if it breaks something critical) Allen Herby ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Good-bye constructor functions?
Herby Vojčík wrote: It can be blessed by Reflect.makeClass(classObject, protoObject) or similar API. It can bring lots of new patterns and cowpaths into the language. If people don't want plain object to be the class, but their existing object x, they do Reflect.makeClass(x, (class {...}).prototype); This can be in some version of `static` syntax in the future. BUt it allows richer outcomes: Not mere: class Foo { ... static { // must be some predefined kind of body } } but instead: claas Foo { ... static Expr; } where Expr can be any object that would be [[Construct]]ified and bound to Foo. So it _can_ be {...} object literal, but it can be anything else, existing object that user want to class-ify, a function, ... and they then can `new x`. They choose what they will use as x for new operator; because now class is one object and constructor is another. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Good-bye constructor functions?
On Jan 7, 2013, at 4:09 PM, Herby Vojčík wrote: Allen Wirfs-Brock wrote: OK, I ready you proposal. I'll summaries it: class Sub extends Super { constructor (...args) { super(...args); //or super.constructor(...args) because they mean the same thing. } } creates an (almost) ordinary object that is the initial value of the Sub binding. The [[Prototype]] of Sub is the value of Super. Sub is created with a prototype property whose value is a new ordinary object whose [[Prototype]] values is Super.prototype. Sub.prototype has a method property named constructor whose value is a super bound method function with the user specified body. There is nothing special about this method, it it just like any other method defined in a class declaration. In particular it does not have a [[Construct]] internal property. The only thing exotic about Sub is that the object has a [[Construct]] internal method. It does not have a [[Call]] internal method. The definition of its [[Construct]] in almost JS pseudo code is: //internal method this.[[Constructor]](argList): let newObj = this.@@create(); //eg Sub.@@create(); let replacementObj = this.prototype.constructor.apply(newObj, argList); if (typeof replacementObj == object replacementObj !== null) return replacementObj return newObj; Implications: Sub === Sub.prototype.constructor evaluates to false. To create a new instance of Sub say: new Sub() The following is a TypeError, because Sub does not have a [[Call]] internal method: Sub() The following is a TypeError, because Sub.prototype.constructor does not have a [[Construct]] internal method: new Sub.prototype.constructor() Sub.prototype.constructor can be called directly or as a method invocation including via a super call a subclass of Sub. It seems to me, all you have accomplished here is to make it illegal to call a class object directly as a function. If we were starting From technical PoV*, yes. Oh, and I fixed the super / new inconsistency. I just don't see such an inconsistency in the current ES6 spec. draft. A constructor is just a function that is usually called with a this value. This is true whether it is called by the [[Constructor]] internal method or via a super/super.constructor call. In either case, the primary purpose of the constructor is to perform initialization actions upon the this value. Where is the inconsistency? The only anomaly I see is that a handful of legacy built-ins do completely different things for new operator originated calls in contrast to regular function calls. There is no confusion in the spec. about this and the mechanism for accomplishing it. However such split behavior can't be declaratively defined in a normal function or class declaration. In the other thread at https://mail.mozilla.org/pipermail/es-discuss/2013-January/027864.html I described how this split behavior an be procedurally described in such declarations and also described how the same technique can be applied to the offending built-in constructors (r any user defined class constructor) to discriminate between initialization and called behavior, even when called via super. The only fix I saw that you made to super was that in you sketch constructor methods defined via a class definition are only used for instance initialization, so there doesn't need to be any code to determine between initialization and call behavior. Constructor behavior is always initialization behavior. However, you don't support non-new invocation behavior, at all, on class objects so you can't use a class to self-host an implementation of, for example, RegExp. You also, don't do any thing to support correct initialization of subclass of built-ins such as RegExp and Number that have different behaviors for called as constructor and called as function. Allen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Good-bye constructor functions?
On Jan 7, 2013, at 3:24 PM, Brendan Eich wrote: Allen Wirfs-Brock wrote: Even if we think we should discourage direct calls to class objects (I think I'm now in that camp) (Why so?) (Assuming you've already read https://mail.mozilla.org/pipermail/es-discuss/2013-January/027864.html) The split constructor/function call behavior of things like Number and RegExp is specified as two distinct procedures that are distinguish based upon the type of invocation. Basally Number represents two different procedures and which one to invoke is contextually determined at the call site. The implementation of this can work by have an alternative entry point, or a special flag parameter associated with each such callable target. Regardless of how it is actually implemented it amounts to the the function Number really representing two different procedures. There is arguably some elegance in being able to use Number and a handful of others as both a coercion function and as a factory for new objects. However, as a general rule, it is probably not such a good idea to allow any arbitrary function to really be two procedure with arbitrarily different behaviors. I suppose you could call it a form or overloading, sdomething which I'm also not particularly a big fan of. Generally, I think programs are easier to understand if each named entity has only a single meaning. When you move from the ES spec. to ES code there currently is no way to declaratively express that we have a two procedure function. You have to have a single function body and try to use a conditional statement to choose between two different code paths within the same body. But there also is no communications channel that conveys to the function body which sort of call context was used for the invocation. In the post linked above, I showed how analysis of the this value along with careful branding of instances and initialization state can adequately duplicate the effective specified behavior of something like Number without direct communication of the nature of the call site. This is similar to the manner in which ad hoc parameter overloading is accomplished by JS programmers. Again, like overloading, it can be accomplished and in a few situations it is desirable, but in general its probably something that should be avoided. So, as a best practice I would generally discourage new/call over-loading. We could try to formalize it. One way would be to make a flag available in the function body that indicating the invocation style (perhaps via predeclared name in the same scope as the function name). Another way would be to have a mechanism (perhaps a @@call or @@new private symbol named property of the function) to associate a second code body with each function. However, either of these introduce the sort of super/new confusion that Herby and Brandon were concerned about. If @@call is the code body used for new, a super() call within it needs to call the super class constructor's @@new code instead of its @@call code like would be used anywhere else. In addition it is adding both Es developer complexity and implementation complexity to potentially all calls. So, I suggest: 1) Constructor normally should only be used to perform instance initialization. 2) In rare circumstances, overloading techniques can be used to make constructor functions choose between performing initialization or non-initialization tasks. However this should be generally discouraged. Allen___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Good-bye constructor functions?
I think it's arguable either way. I mean, the initializer-only behavior advocated by Allen et al makes the class useless when called (as opposed to new'd). I don't think such an opinionated approach is really necessary. One can account for the funky behavior of the built-ins by providing an additional call hook on the class, and leave everything else as is. When the class is called (as opposed to new'd), it would use that hook instead of the constructor. (super() would still use the parent's constructor function.) It could be supported with syntax like so: class C { static(...args) { /* call behavior */ } } I think one could also make a case that the default behavior for such a hook would be to new the class, as some built-ins do (and as many current JS classes attempt to do): class C { static(...args) { return new C(...args); } } This seems like a pretty clean design to me. { Kevin } ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Good-bye constructor functions?
Hello, thinking about class / new / super semantics and its problems, I came with an idea. Big change on the first look, but in fact, imho, not so big and making cleaner model. tl;dr Constructor function were workaround to specify constructor for a class without `class`. Now, having `class` nothing presribes the class being equivalent to its constructor. It always brought confusion and now with `constructor` empowered, it brings two-space problem. The `class` keyword can return plain objects, which have `[[Construct]]` calling the constructor of the class. The rest is in https://gist.github.com/4413009. If you could please read it, it tries to explain and go a little into detail. I feel this is needed to do now, putting constraints away later would be harder, as I write below: Conclusion --- If `class` would decouple the value it produces (the class) from the constructor function, it would create cleaner and more future-friendly model of classes. Coupling class and its constructor function was a necessity in the past, but now they need not to be coupled. Class.prototype.constructor takes all the responsibility for initializing the instance. This change involves little risk (only pieces of code who actually [[Call]] the class for manually initializing it; super-constructor call can be replaced by `super` or `Superclass.prototype.constructor.{call,apply}`). The big spec pieces that involve creation of classes and their instances are already very near and the important pieces need just a few changes. The devil is in the details, the spec is still very 'constructor must be a function'-oriented, but this is mainly in textual parts, algorithms are already generic enough. One important reason to ponder `class` not being constructor function is not conserving this tight coupling which in world of ES6+ with `class` can be seen as antipattern; freeing the contraints now is much easier than doing it later, when this unnecessary coupling is repeated in `class` semantic. Thanks, Herby ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Good-bye constructor functions?
I'll take that occasion to mention that I feel ES doesn't need class at all. We've been using Object.create in a way that allow us to create objects as prototypes for others, on these we add property descriptors, and then make instances off these prototypes without adding anything. We use a create then init and never bother using a constructor function: pure, elegant, prototype inheritance. This pattern works really well and is very natural for people with a class background. Just saying... Benoit On Dec 30, 2012, at 11:47, Herby Vojčík he...@mailbox.sk wrote: Hello, thinking about class / new / super semantics and its problems, I came with an idea. Big change on the first look, but in fact, imho, not so big and making cleaner model. tl;dr Constructor function were workaround to specify constructor for a class without `class`. Now, having `class` nothing presribes the class being equivalent to its constructor. It always brought confusion and now with `constructor` empowered, it brings two-space problem. The `class` keyword can return plain objects, which have `[[Construct]]` calling the constructor of the class. The rest is in https://gist.github.com/4413009. If you could please read it, it tries to explain and go a little into detail. I feel this is needed to do now, putting constraints away later would be harder, as I write below: Conclusion --- If `class` would decouple the value it produces (the class) from the constructor function, it would create cleaner and more future-friendly model of classes. Coupling class and its constructor function was a necessity in the past, but now they need not to be coupled. Class.prototype.constructor takes all the responsibility for initializing the instance. This change involves little risk (only pieces of code who actually [[Call]] the class for manually initializing it; super-constructor call can be replaced by `super` or `Superclass.prototype.constructor.{call,apply}`). The big spec pieces that involve creation of classes and their instances are already very near and the important pieces need just a few changes. The devil is in the details, the spec is still very 'constructor must be a function'-oriented, but this is mainly in textual parts, algorithms are already generic enough. One important reason to ponder `class` not being constructor function is not conserving this tight coupling which in world of ES6+ with `class` can be seen as antipattern; freeing the contraints now is much easier than doing it later, when this unnecessary coupling is repeated in `class` semantic. Thanks, Herby ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss