Good-bye constructor functions?

Herby Vojčík herby at mailbox.sk
Tue Jan 8 00:45:18 PST 2013



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 from [[Construct]]
  - you get default constructor for free; by not defining it explicitly.

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.

> initialization of subclass of built-ins such as RegExp and Number that
> have different behaviors for "called as constructor" and "called as
> function".
>
> Allen

Herby


More information about the es-discuss mailing list