Finding a "safety syntax" for classes

Allen Wirfs-Brock allen at wirfs-brock.com
Sat Mar 24 17:54:47 PDT 2012


On Mar 24, 2012, at 7:28 AM, David Herman wrote:

> On Mar 21, 2012, at 9:13 AM, Allen Wirfs-Brock wrote:
> 
>> On Mar 20, 2012, at 11:32 PM, David Herman wrote:
>> 
>>> 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."
>> 
>> Actually, the semantics is probably more like:  the value bound to the <classMame>  is the function object defined by the <methodDefinition> whose <propertyName> is "constructor".
> 
> Yes, sure. Doesn't change the point: 'constructor' is still a special, distinguished method.

Yes it is.  I think the core issue WRT to identifying this distinguished method with an identifier other than "constructor" concerns how it would relate to an actual method named "constructor".

The conservative course is to use "constructor" as the distinguishing identifier.  It is a direct reflection of the underlying ES<=5.1 chapter 13 and15 "class" model and it doesn't introduce any of these name naming issues.

I primarily favor sticking with "constructor" because the safety/maximally-miminal proposal is all about being conservative...
> ...
>>> 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.
>> 
>> You need to drop the [ ]'s  (although I'm not sure what you meant by them...
> 
> I meant that there is a property of the prototype that you can access via either p["new"] or p["constructor"] or both, depending on which semantics we decide to give. No matter what the surface syntax, any of those semantics is available to us. I say:
> 
>     a) spell it "new" -- ergonomics trumps corner cases; hard cases make bad law
deviates from chapters 13 and 15.  

  (class () {})  isn't interchangeable with (function () {}). Shouldn't it be?
> 
>     b) desugar it to the constructor function and the p.constructor property only

but presumably means that an instance method named new can't be defined using a class definition.  Or, perhaps only if "new" is explicitly string quoted as a property name. "new" is not a totally unreasonable method name.  Also presumably means that defining an instance method named constructor is disallowed.

> 
>     c) i.e., don't create a p.new property -- no more prototype pollution please

I presume you mean it also doesn't define a p.constructor property.  This has similar problems to a).  It deviates from the legacy ES "class model"

Also, most instance inherit from Object.prototype so they will still have an inherited "constructor" property whose value is Object.

> 
>     d) an explicit 'constructor' method overrides the implicit creation of the 'constructor' method but does not define the constructor function
> 
> Why d)? Remember, the .constructor idiom is a *very weak* idiom that many JS programs don't follow. If a JS program has some reason to use 'constructor' for a different purpose, trust them.

I believe the constructor idiom is most commonly not followed today when the prototype property of a function is set to a new object (perhaps defined using an object literal) and correctly dynamically setting the "constructor" property is an extra step that is easily forgotten (an usually as no ill-effects) 

However, with syntactic class definition support (including inheritance) in the language I am sure that we are going to see much more use of OO idioms in ES programs (if that wasn't the case why would be add them).  A very common idiom is to query the "class" of an object.  Just look how frequently you see could like p.class in Java or Ruby code  or( p class) in Smalltalk code. Thew equivalent of this using ES chapter 13/`5 objects is p.constructor.  Which would you prefer to see in future ES code p.constructor===q.constructor or p.new===q.new

BTW, many OO experts including myself, tell people that querying the class of an object in this manner is an undesirable practice.  Maybe even an anti-pattern.  But in reality it is widely done and the negatives all relate to non-fucctional issues like code flexibility and reusability. Developer are going to do it, probably a lot.


> 
>>>> 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. ;)
>> 
>> I would guess that your instinctive response comes from thinking about a "class" is something more than just  a composite of objects.  We can talk more about this later after I respond to Mark
> 
> I think you misread me. My instinctive response agrees with yours, not Mark's.
> 
>> If the value of SOMEEXPRESSION is a constructor function (typeof == "function" && and has a "prototype" property) then the new constructor inherits from SOMEEXPRESSION and the new prototype inherits from SOMEEXPRESSION.prototype.  Otherwise, the new consructor inherits from Function.prototype and the new prototype inherits from SOMEEXPRESSION.  That is essentially the semantics I've defined for
>>        SOMEEXPRESSION <| function () {}

Oops, the above isn't at all a correct statement of the current semantics of SOMEEXPRESSION <| function () {}.  My bad! In particular the "otherwise" clause above is not that of  <|.

I think it is best if I address the inheritance rules is a separate message instead of burying it here.  

> 
> I'm not happy with that semantics, for either classes or <| (I believe others have objected on the list to the special-case semantics for <| as well). Since functions are objects, you can pass functions into contexts that expect an object and those contexts don't need to care whether the object they have is a function or an object. So this will lead to WTFjs moments where people take an object they got passed in from someone else and create a class with it, and it won't be wired up right because they didn't realize the object was a function.
> 
> This kind of special-case ad hoc type testing in the semantics has a bad smell. It reminds me of stuff like the Array constructor that special-cases the number argument.
> 
>> B = do{
>>       let B = SOMEEXPRESSION <| function B(...) { ...};
>>       B.prototype= SOMEEXPRESSION.prototype <| {constructor: B, ...}
>>       B
>> }
>> 
>> (or to be maximally explicit}
>> 
>> B= do {
>>     let B = function B(...) {...};
>>     if (typeof SOMEEXPRESSION == "function" && typeof SOMEEXPRESSION.prototype == "object") B.__proto__=SOMEEXPRESSION;
>>     B.prototype= SOMEEXPRESSION.prototype <| {constructor: B, ...}
>>     B
>> }
> 
> Nit: you'd need to bind the result of SOMEEXPRESSION to a temporary, to avoid duplicating side effects. </macrology nerd>
> 
> Dave
> 



More information about the es-discuss mailing list