Russell Leggett (2013-10-21T19:35:46.000Z)
On Mon, Oct 21, 2013 at 3:17 PM, Benjamin (Inglor) Gruenbaum <
inglor at gmail.com> wrote:

> Russell Leggett <russell.leggett at gmail.com> wrote:
> > https://gist.github.com/genericallyloud/7086380
>
> Very interesting.
>
> >  // 4. use the default if available
>
> what's "the default" in #4? The protocol's default? What's the behavior if
> no matching method is found?
>

The default is something I go into in a little bit of detail further down:

    Collections.defaults({
        each(iterator, context){
            if (this.length === +this.length) {
              for (var i = 0, length = this.length; i < length; i++) {
                //notice we also get to use :: for a simple call replacement
                if (context::iterator(this[i], i, this) === breaker) return;
              }
            } else {
              var keys = this.keys();
              for (var i = 0, length = keys.length; i < length; i++) {
                if (context::iterator(this[keys[i]], this[i], this)
=== breaker) return;
              }
            }
        },

This is defining a sort of default implementation of the method for the
protocol, meaning that other types do not have to implement it in their own
type specific implementation of the protocol.


> Also, can anyone explain why this solves the performance problem scoped
> object extensions have? It still seems like it would have to check the
> environment for protocols and then check the methods on all available
> protocols and do type matching to the type of the method.
>
> `Collections.extend(Array)` seems awfully similar to an array extensions,
> how does the `::` operator resolve the need for expensive lookup, can you
> explain that to me?
>

Yes, the reason is because no new scopes or environments have been created.
The protocol and its methods are simply variables - objects like anything
else. Getting a protocol's method is not really any different from:

import {map} from 'UnderscOOre';
//basically the same as
let {map} = Array.prototype;

//this is effectively the same whether you got it from the protocol or
the function
//pulled off of Array.prototype
arr::map( x => x+2);

The magic is what happens one of these protocol methods. The simplest to
understand/naive approach to this would basically be that inside of a
protocol, for each protocol method, you hold all the methods in a map going
from type to implementation. The actual method used would inspect the
|this| and follow the algorithm and use the maps to figure out which
implementation to use. This would have a penalty - but it would be limited
to the protocol method calls. Scoped extension would apply a penalty to
*all* function calls everywhere. If protocols were natively supported, or
at least some kind of hook for single dispatch on type, I'm pretty sure you
could get function calls that were *at least* as fast as normal prototype
based methods. Even if you didn't get native support, I have a feeling that
something more clever than the naive approach could be used to hit the
sweet spot and get some polymorphic inline caching, but maybe not.

- Russ
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20131021/5439b498/attachment-0001.html>
domenic at domenicdenicola.com (2013-10-28T19:55:57.308Z)
On Mon, Oct 21, 2013 at 3:17 PM, Benjamin (Inglor) Gruenbaum <inglor at gmail.com> wrote:


> what's "the default" in #4? The protocol's default? What's the behavior if
> no matching method is found?

The default is something I go into in a little bit of detail further down:

    Collections.defaults({
        each(iterator, context){
            if (this.length === +this.length) {
              for (var i = 0, length = this.length; i < length; i++) {
                //notice we also get to use :: for a simple call replacement
                if (context::iterator(this[i], i, this) === breaker) return;
              }
            } else {
              var keys = this.keys();
              for (var i = 0, length = keys.length; i < length; i++) {
                if (context::iterator(this[keys[i]], this[i], this) === breaker) return;
              }
            }
        },

This is defining a sort of default implementation of the method for the
protocol, meaning that other types do not have to implement it in their own
type specific implementation of the protocol.


> Also, can anyone explain why this solves the performance problem scoped
> object extensions have? It still seems like it would have to check the
> environment for protocols and then check the methods on all available
> protocols and do type matching to the type of the method.
>
> `Collections.extend(Array)` seems awfully similar to an array extensions,
> how does the `::` operator resolve the need for expensive lookup, can you
> explain that to me?
>

Yes, the reason is because no new scopes or environments have been created.
The protocol and its methods are simply variables - objects like anything
else. Getting a protocol's method is not really any different from:

```js
import {map} from 'UnderscOOre';
//basically the same as
let {map} = Array.prototype;

//this is effectively the same whether you got it from the protocol or
the function
//pulled off of Array.prototype
arr::map( x => x+2);
```

The magic is what happens one of these protocol methods. The simplest to
understand/naive approach to this would basically be that inside of a
protocol, for each protocol method, you hold all the methods in a map going
from type to implementation. The actual method used would inspect the
|this| and follow the algorithm and use the maps to figure out which
implementation to use. This would have a penalty - but it would be limited
to the protocol method calls. Scoped extension would apply a penalty to
*all* function calls everywhere. If protocols were natively supported, or
at least some kind of hook for single dispatch on type, I'm pretty sure you
could get function calls that were *at least* as fast as normal prototype
based methods. Even if you didn't get native support, I have a feeling that
something more clever than the naive approach could be used to hit the
sweet spot and get some polymorphic inline caching, but maybe not.