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 semantics of,
roughly, `[[Call]]([[Get]](.prototype,"constructor"))`, two things can
happen:
  - there is an own "constructor" in .prototype, in is result of [[Get]]
and is [[Call]]ed
  - there is not an own "constructor", so inheritede one is return from
[[Get]] and [[Call]]ed

The second point is equivalent to the case if there was an own
constructor with body `constructor(...args){return super(...args)}`
which is the same (sans return) that what you propose to explicitly
define as the default constructor.

IOW, what you propose as the default constructor is explicit definition
of "I don't have any constructor given, just call the inherited one".

The proposed semantics of [[Construct]] does that naturally, without
needless explicit constructor.

In fact I deem this a feature - you can always tell a class to use
inherited constructor by making it not have an own one (and not only
when defining it, but in runtime as well).

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?

THe proposal of "separate the class and constructor, allow class to be
ordinary object" is the first step. The actual separation of the fused
concepts. In the proposal itself, I proposed the class being the
ordinary object with [[Construct]] of its own calling
.prototype.constructor.

The sketches of second step was in the reply, they go on the line "if
ordinary object can do, anything else can do; I want to be able to
specify it". Concrete things below.

I wrote:
*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".

Here, I say there is no need to stick to plain object for `class`, but other objects could be allowed (ordinary object being just the default of the `class` construct).

It can be blessed by Reflect.makeClass(classObject, protoObject) or
similar API.

Here, I propose there is an API for this. My mistake was that I did not explain its semantics.

I want `Reflect.makeClass(cls, proto)` to do this:

1. Object.define(cls, "prototype", {value: proto, c:false, w: false, e:false}). 2. Define `class` [[Construct]] on cls (one that calls .prototype.constructor).
  3. Return cls. // not needed, really.

OTOH, do the same thing that make an orfinaty object a class in my proposal - define its .prototype and [[Construct]].

For example, the criticized pattern of 'function that does
differently for [[Call]] and [[Construct]]' could now be created as
well. Just 'makeClass' it.

Here I say one can create a `class`-like construct that has its [[Call]], orthogonal from its [[Construct]].

But I do not do it by "injecting" [[Call]] into plain class object - I don't see the need for such operations. Simply take a function (it has its [[Call]] defined well), and make it a `class` - give it consisent .prototype and [[Construct]].

The same thing that was done to plain object in `class` construct.

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)

In the subsequent reply t9 myself, I sketched how this "I want to specify what object would represent my class" could have been fused into language itself:

[The makeClass API] 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, ...

With this, I can do:

  class Foo {
    constructor (...args) { /* initialization code */ }
    ...
    static ...args => { /* invocation code */ };
  }

But it is nothing special, just embedded API from below (or, in other PoV, specifying what object to use in `class` to [[Construct]]ify and return; when not specified, plain object is used).

And once more, this is second step; the first is the separation itself.

Allen

Thanks, Herby

P.S.: The new/super issue can be solved, as well as free default constructor achieved, even without the separation; the critical for the fix and the free default constructor is the new [[Construct]] semantics calling .prototype.constructor. The idea of separation was just the continuation of ideas after this new [[Construct]] behaviour - when F is not [[Call]]ed anymore, why to keep the binding?
_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to