Finding a "safety syntax" for classes

David Herman dherman at mozilla.com
Tue Mar 20 23:32:46 PDT 2012


On Mar 20, 2012, at 6:59 PM, Allen Wirfs-Brock wrote:

> On Mar 20, 2012, at 11:55 AM, David Herman wrote:
> 
>> The part that I'm least happy about is that this doesn't allow for declarative private methods, because if we had the computed syntax like what we're adding to object literals, it would clash with hoisting:
> 
> We can live quite nicely with treating class declaration like const declarations.  The name is logically hoisted so it is visible over the lexical scope, but the name is temporally dead until the class declaration is executed, in statement sequence, as an initializer.  That means that we can have circular references between classes and forward references in inner functions to classes.  The class declaration just need to be initialized before any such reference is actually evaluated. Its TDZ for classes.
> 
> Practically, that means that super classes declarations have to appear in the text before subclasses and that you probably can't tuck all your class declaration at the end of the block/function (yuck, anyway) like you can with function declarations.  I think we can live fine with that.  
> 
> Regarding privates, if classes are initialized in that manner there shouldn't be any problem with the pattern you use for in your example above.  It works exactly the same as for object literals.

Y'know, I think I'm about sold. Somehow it just seemed obvious to me that classes should be pre-initialized like functions. But just about every time something seems obvious to me I'm wrong. I should learn to trust the opposite of my instincts. ;)

Seriously, though, your argument for TDZ makes sense to me.

>> My only syntactic quibble: `constructor` is so inconveniently long. I've argued in the past for `new` as special syntax in class bodies that indicates a constructor.
> 
> Well, it is defining the value of the constructor property.  New syntax (eg, new) could always be added latter.  I don't think we should risk derailing on it now.

Well, hang on now. The 'constructor' syntax is not "just the constructor property." It still carries special status; the semantics of a class says "look for the property called 'constructor' and make that the [[Call]] and [[Construct]] behavior of the class."

Regardless of whether we spell it 'constructor' or 'new' it requires special semantics that says "there's one distinguished method form in the body that determines the constructor of the class." The question is how we spell that. This is 99.9% a surface syntax question. Tou could argue that spelling it 'new' should define a ["new"] method, or a ["new"] method and a ["constructor"] method, or just a ["constructor"] method. If the latter, it's semantically *identical* to spelling it 'constructor'. But even if we chose one of the other two alternatives, the semantic differences here are minor, and the ergonomics of the syntax matter.

Look, it won't be the end of the world if we go with 'constructor'. This particular question won't derail classes. But let's not tax the ergonomics for what would be either a tiny or even non-existent semantic difference.

> So here is my one possibly future hostile (or we need to decide now) issue:
> 
> class A {};
> A.classProperty="A";
> class B extends A {};
> Console.log(B.classProperty);   //"A" or undefined??
> 
> I don't believe Russell said, one way or the other.  Potentially either way could be seen as future hostile if you think the "right" answer is the other one (and we have to do something in this regard).

Agreed; this is a decision we can't defer if we support `extends`.

> Personally I think the answer should be "A" which implies that we have class-side inheritance.   This is a departure from current practice but because "classes are functions" there is no way in ES<=5.1  to set up class side inheritance other than by mutating __proto__.

I always found this the more appealing, but then again, if I'm supposed to be going with the opposite of my instincts (see above), then maybe I should disagree with you. ;)

Seriously, though, Mark's concerns are valid. (On a semi-related note, I intend to try an alternative of the binary data API with no meta-classes.) We can work through this issue.

> We could make an accommodation that this only occurs if the "superclass" object is a function.  Otherwise, the "superclass" is only uses as the [[Prototype]] of the prototype and the constructor function simply inherits from Function prototype.   BTW, I assume the thing the the right of extends  is an expression.
> 
> So, if you could say:
> class B extends A.prototype {}
> 
> if you don't want the class-side inheritance

Wait, I don't see how that could work. If the RHS of `extends` is an arbitrary expression, and we allow the programmer the freedom to provide either a super-class or a super-prototype, how do we know what the prototype's prototype is? IOW, it's ambiguous whether to treat:

    class B extends SOMEEXPRESSION { ... }

as:

    B = do {
        function B(...) { ... }
        B.prototype = SOMEEXPRESSION <| { ... }
        B
    }

or:

    B = do {
        function B(...) { ... }
        B.prototype = SOMEEXPRESSION.prototype <| { ... }
        B
    }

Dave

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


More information about the es-discuss mailing list