A strawman for subclassing

David Bruant david.bruant at labri.fr
Wed Jun 15 13:54:11 PDT 2011


Le 15/06/2011 18:11, Mark S. Miller a écrit :
>
>
> On Wed, Jun 15, 2011 at 8:17 AM, David Bruant <david.bruant at labri.fr
> <mailto:david.bruant at labri.fr>> wrote:
>
>     Le 15/06/2011 16:43, Mark S. Miller a écrit :
>>
>>
>>     On Wed, Jun 15, 2011 at 7:17 AM, David Bruant
>>     <david.bruant at labri.fr <mailto:david.bruant at labri.fr>> wrote:
>>
>>         Le 14/06/2011 07:04, Mark S. Miller a écrit :
>>>
>>>
>>>         On Mon, Jun 13, 2011 at 5:08 PM, David Bruant
>>>         <david.bruant at labri.fr <mailto:david.bruant at labri.fr>> wrote:
>>>
>>>             Le 14/06/2011 01:24, Allen Wirfs-Brock a écrit :
>>>             > On Jun 13, 2011, at 3:46 PM, David Bruant wrote:
>>>             >> Hi,
>>>             >>
>>>             >> The subclassing native constructor issue came out
>>>             several times on the
>>>             >> list and on the web. However, I do not see a strawman
>>>             for this. There
>>>             >> are strawman:array_create and
>>>             strawman:array_subtypes, but no mention of
>>>             >> a generic solution.
>>>             > The <| operator is the proposed solution.
>>>             Oh ok, sorry, I hadn't read it yet. Thanks.
>>>
>>>             > Using <|  instances with arbitrary [[Prototype]]
>>>             values can be created for any type built-in object type
>>>             that has a literal representation.  that is functions,
>>>             arrays, regexps, and less interestingly, strings,
>>>             numbers, booleans.  The resulting instance all have the
>>>             appropriate special internal behaviors that are
>>>             associated with the built-in types.
>>>             It requires the right-hand side to have a specific
>>>             initialization
>>>             syntax. It currently works for all the "classes" you
>>>             cited ("functions,
>>>             arrays, regexps, and less interestingly, strings,
>>>             numbers, booleans"),
>>>             but does not for the current
>>>             harmony:simple_maps_and_sets for which
>>>             there is currently no specific syntax. Consequently,
>>>             these could not be
>>>             subclassed (or I am missing something).
>>>             This would be the case for any other native "class" that
>>>             wouldn't such
>>>             syntax. I'm not sure it's is viable in the long term
>>>             because it will
>>>             require all subclassable (something people will want to
>>>             subclass)
>>>             "classes" to have a syntax.
>>>
>>>             I like the idea of the <| operator, but a generic
>>>             non-syntaxy solution
>>>             would be good too in my opinion. It would also allow
>>>             people to subclass
>>>             host constructors (host objects with [[Construct]]).
>>>
>>>
>>>         We have noticed this issue, and an draft of the classes
>>>         proposal attempted to address it. What's left of that, at
>>>         <http://wiki.ecmascript.org/doku.php?id=harmony:classes#constructor_chaining>
>>>         is:
>>>
>>>         These semantics for constructor chaining preclude defining
>>>         classes that inherit from various distinguished built-in
>>>         constructors, such as Date, Array, RegExp, Function, Error,
>>>         etc, whose |[[Construct]]| ignores the normal object passed
>>>         in as the this-binding and instead creates a fresh
>>>         specialized object. Similar problems occur
>>>         for DOM constructors such as HTMLElement. This strawman can
>>>         be extended to handle such cases, but probably at the cost
>>>         of making classes something more than syntactic sugar for
>>>         functions. We leave that to other strawmen to explore.
>>>
>>         I think that saying "whose [[Construct]] ignores the normal
>>         object passed in as the this-binding and instead creates a
>>         fresh specialized object." is misleading. It seems to imply
>>         that the internal [[Construct]] method of an object always
>>         create a "normal object". It is not true. Internal
>>         [[Construct]] of native functions (ES5 - 13.2.2) create a
>>         normal object (step 1), but there is no constraints for other
>>         objects with an internal [[Construct]] to do so.
>>         Namely, Date, Array, RegExp, Function, Error have a different
>>         approach to [[Construct]]. Host objects aren't This
>>         difference is further emphasis with Harmony proxies which
>>         have the ability to provide two completely different
>>         functions to Proxy.createFunction(handler, call, construct).
>>
>>         By the way, slightly above the paragraph you quote is written:
>>         "Within the body of the constructor, an expression super(x,
>>         y) calls the superclass’s [[Call]] method with thisArg bound
>>         to this constructor’s this and the arguments x and y."
>>         Why has [[Call]] been chosen over [[Construct]]? The latter
>>         makes much more sense in the context of inheritance in my
>>         opinion. And if I ask "A extend B" and B is a function proxy
>>         for which I have provided a custom [[Construct]], I expect
>>         this one to be called, not [[Call]].
>>
>>
>>     As [[Construct]] is currently defined, you can't pass in an
>>     already constructed object for it to further initialize. So it
>>     doesn't make sense for constructor chaining to call the
>>     superclass' [[Construct]], even if the superclass is a function
>>     proxy.
>     Hmm... I think that it's because most current [[Construct]] (ES5 -
>     13.2.2) have steps 1 and 9-10. As far as I know, on ES5,
>     [[Construct]] is only used in the "new" operator; Would it make
>     sense to move steps 1, 9 & 10 to the "new" operator definition
>     (ES5 - 11.2.2)? The object would be passed as the 'this' binding
>     of [[Construct]]. It would allow to create different objects on
>     step 1 depending on context (for instance if the constructor has
>     been created in a "class"-syntax defintion, the object may inherit
>     from something else than Object.prototype). I don't think it would
>     affect how the "new" operator currently works.
>
>     Now that I think about it, there is a weird difference between
>     [[Call]] and [[Construct]]. The former has a 'this' binding and
>     the latter doesn't. Considering the object being constructed as a
>     'this' binding to [[Construct]] could allow what you said ("pass
>     in an already constructed object for it to further initialize") by
>     passing the same 'this' object to several [[Construct]].
>
>     Does this change sounds feasable?
>
>
> I think some such refactoring would be feasible and desirable, and is
> the kind of refactoring I had in mind when I said
>
>     This strawman can be extended to handle such cases, but probably
>     at the cost of making classes something more than syntactic sugar
>     for functions. We leave that to other strawmen to explore.
>
>
> But the refactoring would need to be a bit different than what you
> suggest, in order to both maintain compat with current behavior while
> also allowing subclassing-with-constructor-chaining to, for example,
> Date. Please propose something.
I may be missing something but I have the impression that what I've
suggested works with Date too. In the way I see it, Date.[[Construct]]
is defined in ES5 - 15.9.3.{1,2,3}.
In what I suggested, "new Date();" would create an object (ES5 - 13.2.2
step1 moved to "new" operator), Date.[[Construct]] would be called,
return what is said on ES5 - 15.9.3.{1,2,3}, which is an object and
step9 (of ES5 - 13.2.2 moved to "new" operator) would notice it, and
forget about the object created on step1 (implementations are free to
optimize here) and return the correct object.
I'm not a specialist of Dates. Do they have some specificity I am
missing in my explanation?


I don't know if it's the same issue, but I see a problem with trying to
subclass built-in constructor which create objects with a different
meta-object programming contract than the native object one.
Let's consider for a minute that an object is: [[MOP]] + [[Prototype]]
(looks like Proxy.create signature ;-)). [[Prototype]] being itself an
object ([[MOP]] + [[Prototype]]) or null. Wanting to subclass arrays is
wanting to have the array [[MOP]] with a different [[Prototype]].
Currently, all constructors impose both at the same time.
The current class proposal allow to create a constructor for which one
can choose the [[Prototype]] (both "extends" and "prototype" allow
that). But nothing is said about the [[MOP]].
----
class A(){constructor(){...}}
class B extends A(){constructor(){... super(); ...}}
var b = new B();
// on initialization, an object needs to be created.
// b.[[Prototype]] is B.prototype (which inherits from A.prototype...)
// However, at the time of the call to "new B();", the engine has to
decide which [[MOP]] to use.
// The object with this [[MOP]] will be used as the 'this' value in
B.prototype.constructor and
// consequently the [[MOP]] needs to be decided *before* entering
B.prototype.constructor code.
// Without other inidication, it has to be arbitrarily the native object
[[MOP]] (ES5 - 8.12.{1-9})
// Consequently, there are currently no way to provide another [[MOP]]
with current syntax
----
I do not really know to which extent this is possible, but the same way
we declare a [[Prototype]], could we declare a [[MOP]] to be used all
along in the different constructors chaining?
By providing a handler object? By providing an object from which a
[[MOP]] will be extracted?

David
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20110615/c122b0a5/attachment-0001.html>


More information about the es-discuss mailing list