Bound instance-function vending (was RE: Arrow binding)

Allen Wirfs-Brock allen at wirfs-brock.com
Mon Apr 30 13:51:43 PDT 2012


A bit on Object.extend(obj,{...}) vs. obj.{...}

To get started, just a reminder that we can't really compare the two based on the assumption that the second argument to extend is an object literal as there is no way to syntactically guarantee that.  We have to assume that both arguments to Object.extend are preexisting object that were created in some arbitrary manner.  EG, we are have to compare Object.extend(obj1, obj2) vs. obj1.{...}


On Apr 29, 2012, at 10:10 AM, Axel Rauschmayer wrote:

> ...
> Object.extend would automatically invoke Object.defineMethod() (or similar) for you, to enable super-references (but I doubt there is much use for them in mixins).

Mixin's and inheritance (including) super references are orthogonal concepts.  Some may choose to use only one or the other but they really need to be composable for for those who need to use them in combination. 

The defineMethod point is important.  Equally important is the treatment of private named properties.

To correctly preserve super invocation semantics methods must be processed using Object.defineMethod when they are copied from obj2 to obj1.   For obj1.{...} this isn't a problem because the "extension object" and its properties don't preexist.  They are  just syntax that is processed just like creating completely new properties on obj1 and the semantic rules for those new properties are exactly the same as those that would have been used in an object literal that was providing the initial definition of obj1.  There is no copying semantics of worry about.

One of the syntactic distinctions that is made by obj1.{...}  is the difference between a "method" property and a function valued "state" property.  Consider:

//somewhere else in the program
vender.getSomeCallback = function(a,b) {
        let cb=  function() {...myWeakMap.get(cb).attachment...this.something(a,b)....};
        myWeakMap.set(cb, generateAttachment(a,b));
        return cb;
}

//main example
obj1.{
   callback: vender.getSomeCallback(x,y),
   meth() {
       super.meth();
       this.callback.call(this.target);
 }
   
Note that .{ sees the callback property as a regular "state" property and doesn't use defineMethod semantics to create  it on obj1. The value returned by vender.getSomeCallback is directly used as the value of the callback state property and the identify based annotation mechanism used in the definition of the callback will work just fine.  meth, because it is expressed using concise method syntax is processed as a new method definition that is super bound as if by defineMethod to obj1.

Now consider using extend:

let obj2 = {
   callback: vender.getSomeCallback(x,y),
   meth() {
       super.meth();
       this.callback.call(this.target);
 };

Object.extend(obj1,obj2);

Object.extend doesn't have any visibility of how the properties of obj2 were defined. All it sees is two data properties whose values are functions.  It if used Object.defineProperty to process both properties then methwould be super bound to the wrong object. If it used Object.defineMethod on both properties then the callback function's identify may change.  What Object.extend will have to do is have a heuristic (if the value is  super bound to obj2, then defineMethod it to obj1.  Otherwise, just use defineProperty) that it applies to function valued data properties.   This heuristic  should be adequate for uses that correspond to what could be expressed using .{ but it also allows other cases where the heuristic may produce something other than the desired result.  Those situations simply don't exist for .{

Use of private names with Object.extend is even more problematic. 

Consider something like:

let MixinArrayIterator = (obj) => obj.{
   @iterator() {
          let coll = this;
          return function*() {...yield coll[...}
    }
}
       
which uses the built-in @iterator private name.  This works nicely with  .{ because the private name is used within the .{ special form and doesn't require any runtime reflection on the mixin "object".  However, to do the same with Object.extend would requires that extend has reflective visibility of actual private named properties of obj2. The reflection restrictions on private names exists to support various high-integrity use cases.  In this particular situation, there is nothing actually "private" in the hight-integrity sense about @iterator.  To make this all work with extend we would probably have to re-introduce  the concept of "unique names" which are like private names but without the reflection restrictions. That then would introduces additional complexities.

It is probably possible to support mixin attachment using a  functional form (Object.extend) rather than using a special form (.{} ) but there is a lot more to it than just a simply syntactic substitutions. 

Allen

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


More information about the es-discuss mailing list