An experiment using an object literal based class definition pattern

Brendan Eich brendan at mozilla.com
Mon Aug 8 13:42:39 PDT 2011


On Aug 5, 2011, at 3:29 PM, Allen Wirfs-Brock wrote:

> On Aug 5, 2011, at 12:14 PM, Brendan Eich wrote:
> 
>> On Aug 5, 2011, at 9:32 AM, Allen Wirfs-Brock wrote:
>>>>> I did something similar in my first go at this, but there is a problem...
>>>> 
>>>> Right, the desugaring from class syntax is fragile and error-prone.
>>> 
>>> I not so sure I agree.  It isn't obvious that this code pattern is particularly any more fragile, error prone, or less toolable than some of the class syntax alternatives that have been discussed.  Particular, if a programmer sets up an editor template that inserts:
>>> const classname = subclass <| function ( ) {
>>>    super.constructor();
>>>    this.{
>>> 
>>>    };
>>> }.prototype.{
>>> 
>>> }.constructor.{
>>> 
>>> };
>> 
>> Let's count the hazards and costs:
>> 
>> 1. No hoisted binding.
> 
> I think hoisting classes may be problematic anyway.

That would be surprising, if true, since classes are sugar for prototypal inheritance based on function *declarations*, which hoist.


> Hoisting really isn't need to make forward class references from within method bodies work.  Outside of methods bodies hoisting allows oddities such as:
> 
> class First {
> class:
>   partner: new Second();  //or what ever a data property definition looks like in a class def
>   defaultInstance: new First;  //is this legal
> }

Why is this a problem? Let's desugar this to function declaration syntax:

function First() {
}
First.partner = new Second;
First.defaultInstance = new First;


> class Second{
>   constructor() {
>      this.friend = First.defaultInstance;
>   }
> }

function Second() {
  this.friend = First.defaultInstance;
}

This all works fine thanks to hoisting. Without hoisting, it does not work.


> or even inheritance circularities.

Circularities are errors in any system.


> It isn't obvious to me that there is any actual utility in hoisting class declarations.

It's the same utility for hoisting function declarations: letrec-style binding for mutual recursion, and in general, freedom to use top-down or any order when declaring functions forming a callgraph, instead of having to topologically sort based on caller->callee relation and write function declarations in reverse order.


>> 2. Boilerplate overhead of "const D = B <| function" vs. "class D extends B": 1+2+8=11 vs. 7 (ignoring spaces). Arrow function syntax helps but you still end up with <|...-> cussing.
> 
> I'd argue that the verboseness of "function" is largely an orthogonal issue.  Because <| requires (currently) a literal on the RHS the "function" keyword could conceivably be dropped in that context.  However, I don't think I'd want to advocate for that.  In the end, I don't that 4 does 4 characters would be a significant issue for anybody.

I tried to separate the function shorthand issue in the cited bit above about Arrow function syntax.

Characters count, both degree (even 4) and kind (cussing, i.e., punctuation).


> (BTW, does it bother anybody that the meaning of "extend" to mean "subclass of" in such class declarations is quite different from what seems to be the most common meaning (add properties to an object) of a function named "extend" in JS libraries and frameworks.)

Slightly. The words differ. Class syntax uses 'extends', Prototype et al. use 'extend'.

Extension by copying vs. extension by delegation are two varieties of extension. Some call the copying form "composition".

Indeed classes with traits in heritage clauses have been proposed to use 'extends', same as for the case of a superclass. Only for the case of a super-prototype was 'prototype' proposed as the alternative linking keyword.

Perhaps we should use a different contextual keyword, but it's a close call at best. Worst case, it's actually better to use 'extends' and we shouldn't mess with existing terminology in either place (class heritage clause, Object.extend imperative API).


>> 3. As you already mentioned contra Axel, one cannot transpose .prototype. and .constructor. parts. Worse, if you don't have class methods, you have to end with a runt .constructor followed by a ;.
> 
> My argument is that once you learn the pattern this isn't an issue.

My argument is simply that class syntactic sugar can be considered on top of <| and .{ -- there is no "exclusive OR" mandate here. Could be "AND also" ;-).

/be

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20110808/44c30018/attachment.html>


More information about the es-discuss mailing list