An "extend" operator is a natural companion to <|

Allen Wirfs-Brock allen at wirfs-brock.com
Mon Jul 18 12:16:00 PDT 2011


On Jul 18, 2011, at 11:32 AM, Brendan Eich wrote:

> Hawt.
> 
> A bit rough in that LHS <& RHS mutates LHS, whereas LHS <| RHS is pure and produces a new object (which could be optimized to mutate RHS, note well!). Both <| and <& are operators, to support chaining. Would it be better for <& to be pure as <| is, and make an assignment operator form, LHS <&= RHS, that expands to LHS = LHS <& RHS?

One way to think about <& is as a more declarative and more concise alternative to Object.defineProperties. I think there are plenty of use causes for the mutating extends.  For example, adding properties to the prototype object of a function.  Another issue is that the object you need to extend may already have been captured somewhere.  For example, a super.constructor call might have registered the object in a WeakMap and if <& created a new instance you would loose the identify relationship.

In practice, I think most of the more declarative uses such as adding own properties can be implemented (and perhaps even specified) such that no extra objects need to be created.

One thing that I think is still open is whether the RHS needs to be an ObjectLiteral or whether any object producing expression should be allowed.  All the use cases  I have explored use an ObjectLiteral but I don't see any hazard (like would be the case if <| mutated the LHS [[Prototype]]) with allowing a non-literal value of the RHS.

> 
> Anyway, if I have <& right, then:
> 
> class SkinnedMesh extends THREE.Mesh {
>  constructor(geometry, materials) {
>    super(geometry, materials);
> 
>    public identityMatrix = new THREE.Matrix4();
>    public bones = [];
>    public boneMatrices = [];
>    ...
>  }
> 
>  update(camera) {
>    ...
>    super.update();
>  }
> }
> 
> from http://wiki.ecmascript.org/doku.php?id=harmony:classes would desugar to:
> 
> function SkinnedMesh(geometry, materials) {
>  return super(geometry, materials) <& {
>    identityMatrix: new THREE.Matrix4(),
>    bones: [],
>    boneMatrices: [],
>    ...
>  };
> }
> 
> SkinnedMesh.prototype = THREE.Mesh.prototype <| {
>  update(camera) {
>    ...
>    super.update();
>  }
> };

yes, plus you need a
   constructor: SkinnedMesh
in the object literal used to defined SkinnedMesh.prototype

> 
> Class-side inheritance could be done via
> 
> let SkinnedMesh = THREE.Mesh <| (function (geom, mats) { ... } <& { classMethod() {...} });
> 
> This loses the hoisting of SkinnedMesh that the function declaration desugaring gained "for free" -- another rough spot to smooth out.
> 
> There's still a usability argument for class syntax, certainly.

Perhaps, but a simpler class syntax is arguably a better class syntax and as I mentioned "static" properties are relatively rare.  If the class declaration is taking care of building both the class and instance side prototype chains then

class SkinnedMesh extends THREE.Mesh {
    ...
    } <& {
    classMethod() {...}
};

would take care of them without complicating the class declaration body.

> 
> Class syntax is also, for some folks, an attractive nuisance because of single-inheritance OOP being oversold and very often the wrong paradigm. But <& helps there too -- one can more conveniently make mixins (I must mean <&= here, of course).
> 
> The last conflicting name wins, or so it seems from what you've written. The compositional answer there is to seal properties you don't want anyone to redefine by a conflicting extend operation. I'm assuming the internal method used to update the LHS or populate the new copy of it is [[DefineOwnProperty]], as with ES5 object literals, and not [[Put]].

   Exactly. Like I mentioned above, <& is I proposed it (your <&=) is essentially a syntactic shorthand for Object.defineProperties. 

Allen





More information about the es-discuss mailing list