This is partially a followup to the thread:  An "extend" operator is a natural 
companion to <|

Several interesting ideas developed out of informal conversations during last 
week's TC-39 meeting.

The first ideas concerns the  <| operator.  As currently defined at 
http://wiki.ecmascript.org/doku.php?id=harmony:proto_operator a usage such as:
    function foo () {};
    foo.prototoype.method = function() {};
    const bar = foo <| function() {};

assigns to bar a function whose [[Prototype]] internal property is the value of 
foo.  However, as current specified the [[Prototype]] of bar.prototype will be 
Object.prototype.  In other words,  bar inherits from foo but bar.prototype 
doesn't inherit from foo.prototype.  That seems unlikely to be the desired 
behavior in most situations.  We can fix that by specifying that if the RHS of 
<| is a function expression and the LHS is an object with a "prototype" 
property then the object that is the value of the "prototype" property of the 
new function object inherits from LSH.prototype rather than Object.prototype.  
I'll assume that definition for the rest of this posting.  It means that for 
the above statements, the following identities hold:
   Object.getPrototypeOf(bar)===foo;  //true
   Object.getPrototypeOf(bar.prototype)===foo.prototype;  //true

Also note that the LHS does not need to be a function for this to work.  You 
might have defined:
  const foo = Function.prototype <| {
     aConstructorProperty: whatever,
     prototype: {
       anInstanceProperty: whatever
     }
  };

and the above identities would still hold. 
   
Another idea was an alternative way to express the "extend" operator for 
literal property definitions.  Doug Crockford suggested the following syntax:

    obj.{
       prop:1,
       prop:2
       }

This takes the properties defined in the object literal and adds then to obj 
(or replaces like named already existing properties).  An exception is thrown 
if they can't all be added.  Essentially .{ } is a postfix operator that 
extends an object with additional properties.

Using these two new ideas and other active object literal enhancement proposals 
it is pretty easy to compose a class-like declaration.  For example the 
SkinnedMesh from the http://wiki.ecmascript.org/doku.php?id=harmony:classes 
proposal can be code as:

  const SkinnedMesh = THREE.Matrix4.Mesh <| function(geometry,materials){
    super.construtor(geometry,materials);
    this.{
      identity.Matrix: new THREE.Matrix4(),
      bones: [],
      boneMatrices: []
    };
  }.prototype.{
    update(camera) {
      ...
      super(update);
    }
  }.constructor.{
    default(){
      return new this(THREE.defaultGeometry,THREE.defaultMaterials);
    }
  };
        

Note that I added a constructor (ie, "static") method as one wasn't included in 
the original example.  

This definition is very similar to the one in the classes proposal but in 
completely defined using object literals.  Here is the generalized code pattern 
for such "class" definitions:

const className = superClass <| function(/*constructor parameters*/) {
  /*constructor body*/
  super.constructor(/*arguments to super constructor*/);
  this.{
    /* per instance property declarations */
  };
  /* other constructor code */
}.prototype.{
  /*instance properties defined on prototype*/
).constructor.{
  /*class (ie, constructor "static") properties */
};
 
Another idea that was discussed and which I think we are near to reaching 
consensus on is how to define a private named property in an object literal (or 
class declaration). As has been suggested in the past, it would be done by 
using a bracketed expression in the property name position.  For example:

  const pname1 = Name.create();  //create a new private name and bind it to a 
const
  const pname2 = Name.create();  
  let obj = {
    regularName: 0,
    [pname1]: 1,  // a data property with a private name
    [pname2]() {reurn this[pname1]} //a method data property with a private name
  };

I wanted to evaluate these feature proposals and coding patterns with something 
more substantial then a 5 line synthetic example.  What would it be like to 
actually write real class based code using them.  So, as an experiment, I 
decided to see what the Smalltalk-80 collection hierarchy would look like coded 
in ES.next using these ideas and patterns.   The Smalltalk-80 collections 
hierarchy is a good test case because it is a well-known class hierarchy that 
exercises pretty much the full gambit of features that are useful in building 
complex single inheritance hierarchies. It is relatively deep (6-levels in my 
experiment), uses both class and instance side inheritance, make extensive use 
of super method calls, "protected" methods, and inherited private instance 
state.  Its basic design has been in use for over thirty years and is one of 
the original object-oriented hierarchies that pretty much established the 
pattern of how to used OO implementation inheritance. You may quibble about 
some of the design approaches and implementation techniques used in this 
vintage hierarchy, but I think it is reasonable to expect that any fully 
featured dynamic OO language should be expressive enough to support an 
implementation of this class hierarchy.

My implementation is based upon the description in chapter 13 of the 
Smalltalk-80 "Blue Book" (http://stephane.ducasse.free.fr/FreeBooks/BlueBook/ ) 
along with an occasional peak at the open source Squeak Smalltalk-80 
implementation (http://ftp.squeak.org/1.3/SqueakV1.sources.gz ).  It isn't a 
complete implementation but is complete enough to get a pretty good feel for 
what it is like to write and read code written in this style. Oh, and did I 
mention, that is is completely untested and probably wouldn't even syntax check 
correctly if I had a parser for this ES.next alternative.

The full source file of this experiment is at  
https://github.com/allenwb/ESnext-experiments/blob/master/ST80collections-exp1.js
 . I encourage you to look closely at the whole thing, think about these coding 
pattens for defining "classes", and to make your feedback here. 

As teaser, here is the code for one of the classes:

/*-------------------------- ArrayCollection --------------------------*/
/* I am an abstract collection of elements with a fixed range of integers
(from 1 to n>=1) as external keys.
*/
export const ArrayedCollection = SequenceableCollection <| function(elements=0) 
{
  super.constructor(elements);
}.prototype.{
  //accessing protocol
  get size() {return this.basicSize},
  at(index) {return this.basicAt(Math.floor(index))},
  atPut(index,value) {return this.basicAtPut(Math.floor(index),value)},
  //adding protocol
  add(newObject) {this.shouldNotImplement()},
  //protected methods for storage access
}.constructor.{
  newWithAll(size,value) {
    return (new this(size)).atAllPut(value);
  },
  with(...args) {
    const newCollection = new this(args.length);
    let i = 1;
    args.forEach(function(element) {newCollection.atPut(i++,element)});
    return newCollection;
  },
  className: "ArrayedCollection",
 };


_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to