any toMethod() use case ?

# Andrea Giammarchi (10 years ago)

Reading this: esdiscuss.org/topic/november-19-2013-meeting-notes#content-6

I was wondering if anyone would be so kind to provide a concrete/real-world use case for toMethod() since I am having hard time to imagine a scenario where a super can be so easily invoked, being (AFAIK) multiple inheritance not allowed right now in ES6 specs.

When exactly would a piece of code invoke a super withou knowing which one is it?

Wouldn't this lead to potential infinite loop within the super invocation itself if referenced from a subclass that was already using toMethod() within the super() itself?

Thanks for any clarification.

# Allen Wirfs-Brock (10 years ago)

On Dec 2, 2013, at 7:51 PM, Andrea Giammarchi wrote:

Reading this: esdiscuss.org/topic/november-19-2013-meeting-notes#content-6

I was wondering if anyone would be so kind to provide a concrete/real-world use case for toMethod() since I am having hard time to imagine a scenario where a super can be so easily invoked, being (AFAIK) multiple inheritance not allowed right now in ES6 specs.

toMethod is a low level primitive that can be used to implement things like Object.mixin in a manner that works correctly with functions that reference super.

When exactly would a piece of code invoke a super withou knowing which one is it?

Anytime you want to before/after/around wrap a call to the the method that would otherwise be invoked.

Anytime you want to define a "mixin" on an object with a known interface but multiple implementations.

Wouldn't this lead to potential infinite loop within the super invocation itself if referenced from a subclass that was already using toMethod() within the super() itself?

There is a potential for a super invocation-based unbounded recursion loops, but it doesn't require the use to toMethod to make it occur. consider

class P { }
class C extends P {
    foo() {
        console.log("f");
        super();
    }
}
P.prototype.foo=C.prototype.foo;
(new C).foo();   //infinite recursion outputting lines of f's

If instead you coded:

P.prototype.foo = C.prototype.foo.toMethod(P.prototype);

you would not get the infinite recursion, instead you would get two f's followed by a "method not found" .

# Andrea Giammarchi (10 years ago)

Thanks Allen but you mentioned Object.mixin twice while this has been abandoned so I still have hard time thinking about a concrete real-world scenario where toMethod is needed. Do you have any code example? The one in the meeting note is very likely nothing nobody would ever write.

If instead you coded:

here my problem ... toMethod cannot assume I know what people coded instead ... toMethod looks like a foot-gun able to bring many shenanigans.

If I have an explicit SuperConstructor.prototype.method.call(this, arg) inside an inherited class method I know what I am invoking, I know what happen to that instance in that moment, I can trust the execution of that code.

This is what TypeScript is doing to, as far as I remember.

The moment super becomes dynamically mutable due toMethod call:

  1. TypeScript will fail as it is
  2. I have no idea what the super method expects from my instance neither what it does being kinda privileged too being promoted as parent
  3. if every transpiler has to create this magic we will end up in a much slower intermediate code as it is for this.super(arg) resolved dynamically in [redefine.js][1] instead of the direct super.proto.method.call

So far I don't see any win, plus I cannot think of any scenario where this magic is needed/wanted/useful.

Thanks again for your patience

1: WebReflection/redefine#classes

# Brendan Eich (10 years ago)

Andrea Giammarchi wrote:

Thanks Allen but you mentioned Object.mixin twice while this has been abandoned

No, deferred -- but why does ES6 status matter? You seem to be impeaching toMethod not because it isn't useful, as Allen showed, but because something that would need it if self-hosted (Object.mixin) isn't standardized in ES6 as well. That doesn't make sense. If Object.mixin is in ES6, one use-case for toMethod is already "done".

Anyway, Object.mixin should be done on github and win adoption. Probably libraries will do their own variations. They all need toMethod to cope with super.

# Domenic Denicola (10 years ago)

Perhaps it would be helpful if someone showed how to implement Object.mixin using Function.prototype.toMethod?

# Brendan Eich (10 years ago)

Andrea Giammarchi wrote:

The moment super becomes dynamically mutable due toMethod call:

  1. TypeScript will fail as it is

Here you seem to imply toMethod mutates 'super' in a function object. It does not. It creates a fresh function object with the given [[Home]].

# Andrea Giammarchi (10 years ago)

I am the author of one of those github Object.mixin "wannabe shim" and this is why I'd like to know how crucial/relevant would this toMethod be for the implementation because it's un-shimmable for what I can tell if not swapping a global super reference at runtime per each wrapped method: a complete no-go that TypeScript itself should never adopt in my opinion.

Moreover, I believe mixins should not bring multiple inheritance but rather enrich objects and if a mixin calls its parent, that should be the expected parent no matter where the mixin has been used to enrich another object.

This is why I am having some difficulty imaging a scenario where toMethod is needed and still I haven't seen a code example that would reflect some real-world scenario/case.

I am just trying to understand and nothing else. Thanks for any example/piece of code that shows why toMethod is needed/wanted/desired instead of static/explicit super.method calls.

# Domenic Denicola (10 years ago)

I wish we'd never switched from Object.define to Object.mixin, since people have such an emotional attachment to their conception of what the word "mixin" means and it causes reactions like this. In that sense, I'm glad it's gone from ES6.

# Andrea Giammarchi (10 years ago)

trying to understand in order to update a polyfill != emotional attached to mixin

I find mixin useful for what it has always done until now (that is temporarily gone and it is un-shimmable due this new toMethod entry for what I can tell so it will likely not be adopted later on)

Still very looking forward for a concrete example where super() that points to a different one after toMethod() is desired, thanks.

# Allen Wirfs-Brock (10 years ago)

On Dec 3, 2013, at 11:08 AM, Domenic Denicola wrote:

Perhaps it would be helpful if someone showed how to implement Object.mixin using Function.prototype.toMethod?

I don't have the time right now, but as a rough approximation take the most recent specification of MixinProperties (which is the guts of Object.mixin) and rewrite in in JS. Replace each call of RebindSuper(x,y) with x.toMethod(y). Replace each call of SameValue(GetSuperBinding(x), source) with (typeof x === "function"),

# Andrea Giammarchi (10 years ago)

what do TypeScript folks think about this?

To me it looks like I can drop the polyfill until a meaningful toMethod exists in ES5

Reading through, I missed the part the super binding could have been redefined in any case.

I still will very appreciate an example ... maybe even an abstract one that talks about real-world scenarios, thanks.

# Allen Wirfs-Brock (10 years ago)

On Dec 3, 2013, at 11:28 AM, Andrea Giammarchi wrote:

I find mixin useful for what it has always done until now (that is temporarily gone and it is un-shimmable due this new toMethod entry for what I can tell so it will likely not be adopted later on)

Object.mixin was always unshimmable because of the its super rebinding semantics. See people.mozilla.org/~jorendorff/es6-draft.html#sec-mixinproperties

Still very looking forward for a concrete example where super() that points to a different one after toMethod() is desired, thanks.

The the last meeting we also decided to eliminate the comparator argument to the Map constructor because it only was useful for the case where you wanted to memoize -0.

Here is a "mixin" object that can be used to selectively add that support to any Map instance to any Map subclass's prototype or to any object that exposes the Map interface:

let MapM0 = (memo=> ({
   has (key) {
         if (Object.is(key, -0.0) return memo.has(this);
         return super(key);
   },
   get (key) {
         if (Object.is(key, -0.0) return memo.get(this);
         return super(key);
   },
   set (key, value) {
         if (Object.is(key, -0.0) return memo.set(this, value);
         return super(key, value);
   }
}))(new WeakMap);

Just mixin (using toMethod, or your own implementation of Object.mixin) the properties of MapM0.

# Andrea Giammarchi (10 years ago)

Thanks Allen, however you know which super will be and what that operation does once invoked, right? Isn't toMethod bringing scenarios where you don't know what that would do?

Isn't that mixin unusable for any other kind of object that is not inheriting Map? The latter is the one that I don't get ... I can use toMethod for something that will simply break or not behave as expected, while what I'd like to do is to be sure that the Map method is used and nothing else.

# Allen Wirfs-Brock (10 years ago)

Nope. There is absolutely no dependency upon Map in the code I wrote. Each of the methods I showed have a dependency upon finding a like-named property up the prototype chain of the object it gets bound to (via toMethod) and implicitly assumes such properties correctly implement appropriate map-like behavior. They do not depend upon finding Map.prototype on that prototype chain or upon finding the built-in implementation of the corresponding methods.

These assumptions are no more risky then the assumptions I would have been making if instead of a super call I had coded:

return Map.prototype.has.call(this, key);

When I code that I assume that, at runtime, a 'has' property will be found on Map.prototype, that the value of that property is a function, and that the function implements that contract that I'm expecting. Saying super(key) or even super.has(key) makes the same assumptions but is not tied to any one particular inheritance hierarchy.

# Brandon Benvie (10 years ago)

On 12/3/2013 11:29 AM, Allen Wirfs-Brock wrote:

I don't have the time right now, but as a rough approximation take the most recent specification of MixinProperties (which is the guts of Object.mixin) rewrite in in JS. Replace each call of RebindSuper(x,y) with x.toMethod(y). Replace each call of SameValue(GetSuperBinding(x), source) with (typeof x === "function"),

This should be pretty close

function Assert(test) {
   if (!test) {
     throw new Error("Assertion failure");
   }
}

function Type(value) {
   switch (typeof value) {
     case "string":
       return "String";
     case "number":
       return "Number";
     case "boolean":
      return "Boolean";
     case "symbol";
       return "Symbol";
     case "undefined";
       return "Undefined";
     case "object":
       if (value === null) {
         return "Null";
       }
     case "function":
       return "Object";
   }
}

// 6.2.4.2 IsDataDescriptor
function IsDataDescriptor(Desc) {
   // step 1
   if (Desc === undefined) {
     return false;
   }
   // step 2
   if (!("value" in desc || "writable" in desc)) {
     return false;
   }
   // step 3
   return true;
}

// 7.4.2 IteratorNext
function IteratorNext(iterator, value) {
   // Step 2-3
   const result = iterator.next(value);
   // Step 4
   if (Type(result) !== "Object") {
     throw new TypeError();
   }
   // Step 5
   return result;
}


// 7.4.3 IteratorComplete
function IteratorComplete(iterResult) {
   // Step 1
   Assert(Type(iterResult) === "Object");
   // Step 2
   const { done } = iterResult;
   // Step 3
   return !!done;
}

// 7.4.5 IteratorStep
function IteratorStep(iterator, value) {
   // Step 2-3
   const result = IteratorNext(iterator, value);
   // Step 4-5
   const done = IteratorComplete(result);
   // Step 6
   if (done) {
     return false;
   }
   //Step 7
   return result;
}

// 19.1.2.15 Object.mixin
function mixin(target, source) {
   // step 1-2
   const to = ToObject(target);
   // step 3-4
   const from = ToObject(source);
   // step 5
   return MixinProperties(to, from);
}

// 19.1.2.15.1 MixinProperties
function MixinProperties(target, source) {
   // step 1
   Assert(Type(target) === "Object");
   // step 2
   Assert(Type(source) === "Object");

   // step 3 - 4
   const keys = Object.getOwnPropertyKeys(source).values();
   // step 5
   let gotAllNames = false;
   // step 6
   let pendingException = undefined;

   // step 7
   while (gotAllNames === false) {
     // step 7.a - 7.b
     const next = IteratorStep(keys);
     // step 7.c
     if (next === false) {
       gotAllNames = true;
     // step 7.d
     } else {
       // step 7.d.i - 7.d.ii
       const nextKey = IteratorValue(next);
       // step 7.d.iv
       const desc = Object.getOwnPropertyDescriptor(source, nextKey);

       // step 7.d.v
       if (desc !== undefined) {
         // step 7.d.v.1
         if (IsDataDescriptor(desc)) {
           // step 7.d.v.1.a
           const propValue = desc.value;
           // step 7.d.v.1.b
           if (typeof propValue === "function") {
             try {
               // step 7.d.v.1.b.i
               const newFunc = 
MixinProperties(propValue.toMethod(target), propValue);
               // step 7.d.v.1.b.iii
               desc.value = newFunc;
             } catch (e) {
               // step 7.d.v.1.b.ii
               if (pendingException === undefined) {
                 pendingException = e;
               }
             }
           }
         } else {
           // step 7.d.v.2.a
           const getter = desc.get;
           // step 7.d.v.2.b
           if (typeof getter === "function") {
             try {
               // step 7.d.v.2.b.i
               const newFunc = MixinProperties(getter.toMethod(target), 
getter);
               // step 7.d.v.2.b.iii
               desc.get = newFunc;
             } catch (e) {
               // step 7.d.v.2.b.ii
               if (pendingException === undefined) {
                 pendingException = e;
               }
             }
           }

           // step 7.d.v.2.c
           const setter = desc.set;
           // step 7.d.v.2.d
           if (typeof setter === "function") {
             try {
               // step 7.d.v.2.d.i
               const newFunc = MixinProperties(setter.toMethod(target), 
setter);
               // step 7.d.v.2.d.iii
               desc.set = newFunc;
             } catch (e) {
               // step 7.d.v.2.d.ii
               if (pendingException === undefined) {
                 pendingException = e;
               }
             }
           }
         }
       }

       try {
         // step 7.d.v.3
         Object.defineProperty(target, nextKey, desc);
       } catch (e) {
         // step 7.d.v.4
         if (pendingException === undefined) {
           pendingException = e;
         }
       }
     }
   }

   // step 8
   if (pendingException !== undefined) {
     throw pendingException;
   }

   // step 9
   return target;
}
# Brian Di Palma (10 years ago)

This is sort of OT but not too much.

I have a concern about how mixins will be implemented in ES. It concerns clashing property identifiers in mixins and the classes taking their behavior.

By way of a simple example an Emitter could have a local property "this.listeners" for its own internal purposes. Likewise a class that mixes in Emitter could also have a local property "this.listeners".

I was wondering if the delayed Object.mixin implementation included some sort of sandboxing to deal with this case or if it was left to the discretion of the developer?

I guess the obvious suggestion is to use Symbols in Emitters, does this mean sandboxing will not be a consideration for Object.mixin?

You could imagine a new version of the Emitter introducing a new identifier that could clash and lead to tricky bugs especially if the mixin did not define the identifier until certain conditions were met..

# Andrea Giammarchi (10 years ago)

Allen, my concern is that if I borrow that method I still assume that Map.prototype.has is what I expect.

This is an assumption I can give for granted once I Object.freeze(Map.prototype) while I cannot do the same assumption once I use super.has since I don't know what will happen ... I won't even know if that instance has a super with a has method while I am sure Map.prototype.has exists.

What Brandon wrote after is not a real-world use case for toMethod, rather the justification for the only place it would be used or it has been created for, the deferred Object.mixin so I am still not sold on why we need a runtime/dynamic resolution for super and still I wonder how much this will impact TypeScript logic and performance once transpiled.

I hope somebody from Microsoft will find a minute to share thoughts on this.

# Dean Landolt (10 years ago)

On Tue, Dec 3, 2013 at 4:24 PM, Brian Di Palma <offler at gmail.com> wrote:

This is sort of OT but not too much.

I have a concern about how mixins will be implemented in ES. It concerns clashing property identifiers in mixins and the classes taking their behavior.

By way of a simple example an Emitter could have a local property "this.listeners" for its own internal purposes. Likewise a class that mixes in Emitter could also have a local property "this.listeners".

I was wondering if the delayed Object.mixin implementation included some sort of sandboxing to deal with this case or if it was left to the discretion of the developer?

I guess the obvious suggestion is to use Symbols in Emitters, does this mean sandboxing will not be a consideration for Object.mixin?

You could imagine a new version of the Emitter introducing a new identifier that could clash and lead to tricky bugs especially if the mixin did not define the identifier until certain conditions were met..

It sounds like you're looking for Symbols.

# Erik Arvidsson (10 years ago)

Since the class syntax is purely declarative it leaves people who need to do abstractions of classes behind. Imagine something like Backbone or Ember.

backbonejs.org/#Model, emberjs.com/guides/models

If they want to use super their class abstractions need to be updated to use toMethod internally.

# Andrea Giammarchi (10 years ago)

What if they never needed to do that since everything was workign already as meant?

# Brandon Benvie (10 years ago)

On 12/3/2013 10:28 PM, Andrea Giammarchi wrote:

What if they never needed to do that since everything was workign already as meant?

They can continue ignoring the new things from ES6 and nothing will break.