Scoped binding of a method to an object

Benjamin (Inglor) Gruenbaum inglor at gmail.com
Sun Oct 13 14:28:51 PDT 2013


Thanks, this really helped me understand the underlying issue here forcing
dynamic resolution of scope here. It sounds a lot harder than I initially
thought

I have to say the fact this is so deeply rooted in the language semantics
is somewhat surprising to me. That said, I think I see why behavioral
typing would be act like this and I expect this to still be a lot harder
than I imagine.

> Here is a counterexample based on the strawman's syntax:
> ...

When I pass a parameter `a` to a function and call `a.where` , whether or
not it's an extension method - don't I still have to do dynamic scope
resolution? Doesn't it still have to walk the prototype chain until I find
`.where` - just like if I pass an array-like with `.splice` and send it to
a method using it and it has to figure out it's not Array.prototype.splice?

Off the top of my head - what about for the duration of the scope of these
methods we  "have a prototype right after Array.prototype" - in this
scenario. Doesn't this make `c` go to Object.prototype.where and then
String.prototype.select and all `b` does is another prototype chain look up
before reaching the extension? This is probably another naive suggestion
though.




On Sun, Oct 13, 2013 at 10:15 PM, Brendan Eich <brendan at mozilla.com> wrote:

> Benjamin (Inglor) Gruenbaum <mailto:inglor at gmail.com>
>> October 13, 2013 11:19 AM
>>
>> Keeping a third parameter beyond object and property name seems
>> unnecessary.
>>
>
> Here is a counterexample based on the strawman's syntax:
>
> // In module LINQ's body:
>   extension Array.prototype {
>     where: Array.prototype.filter,
>     select: Array.prototype.map
>   }
>   export function query(a, w, s) {
>     return a.where(w).select(s);
>   }
>
> import {query} from "LINQ";
>
> Array.prototype.select = () => "oops";
>
> var b = readData();
> var w = ..., s = ...;
> var r = query(b, w, s);
>
> // So far, so good. Now let's try an array-like:
> var c = { length: 0 };
>
> // and let's be mean:
>
> Object.prototype.where = () => "oops for real!";
>
> String.prototype.select = x => x; // identity function
>
> // Fill c from b and query c with w and s:
> b.forEach(e => c[c.length++] = e);
> var s = query(c, w, s);
>
> // This must hold:
> assertEqual(s, "oops for real!");
>
> The only way to make this work is for the code in LINQ's body, in function
> query, to look for 'where' in 'a' (whose type is not known statically) by
> passing a token for the extended scope of module LINQ's body.
>
> When 'a.where' is evaluated to call the method, if 'a' is an instance of
> Array.prototype with no shadowing 'where', in particular when it refers to
> 'b', then the extension method = Array.prototype.filter is found.
>
> When 'a' refers to 'c', however, the code in LINQ does not know this _a
> priori_, so again the lookup of 'where' in c must pass the scope token. But
> this time, because c is an array-like Object instance that does not
> delegate to Array.prototype, the extension 'where' method is not found.
>
>
>  In my likely naive eyes, dynamic  `this` gives us great power here.
>> Thinking about other languages that deal with the problem. As far as I
>> remember C# extension methods are just (really nice) syntactic sugar for
>> statics.
>>
>
> C# has static types and name lookup rules. JS has neither in full,
> although its name lookup rules are static within functions (and blocks in
> ES6), excluding 'with'.
>
>
>  Is it difficult to convert something like:
>>
>> ```
>> Array.prototype.last = function(){ return this[this.length-1] }
>> ```
>>
>> To something like
>>
>> ```
>> function Array$prototype$last(param1){ return (function(){ return
>> this[this.length-1] }).call(param1); }
>> ```
>>
>> ?
>>
> You have not defined "convert", and there's no client code that calls
> 'last' on a given array instance.
>
> Another way of saying this: your name mangling does nothing to help an
> array ('b' in my example) find its method. There's no static type, no class
> or traits. The lookup in LINQ to call a.where is fully dynamic. Same for
> any use of your last method. The name rendezvous must be via prototypal
> lookup.
>
> If you wrote client code in scope of the 'last' extension, e.g.
>
>   var b = [1, 2, 3];
>   alert(b.last());
>
> and expected some whole-program transformation (which we do not perform in
> JS, too much ambiguity -- think of computed property names, never mind
> 'with' and the global object and eval!) that results in
>
>   var b = [1, 2, 3];
>   alert((b instanceof Array) ? Array$prototype$last.call(b) : b.last());
>
> then you have essentially added the third (scope token) parameter by code
> expansion. Whether ?: or an if statement or polymorphic lookup is used,
> that's real added runtime cost, and it won't fly.
>
> /be
>
>
>> ---------- Forwarded message ----------
>> From: Erik Arvidsson <erik.arvidsson at gmail.com <mailto:
>> erik.arvidsson at gmail.**com <erik.arvidsson at gmail.com>>>
>> To: Brendan Eich <brendan at mozilla.com <mailto:brendan at mozilla.com>>
>> Cc: es-discuss <es-discuss at mozilla.org <mailto:es-discuss at mozilla.org**>>
>> Date: Sun, 13 Oct 2013 13:32:23 -0400
>> Subject: Re: Scoped binding of a method to an object
>> We did proposes this back in 2011
>>
>> http://wiki.ecmascript.org/**doku.php?id=strawman:scoped_**
>> object_extensions<http://wiki.ecmascript.org/doku.php?id=strawman:scoped_object_extensions>
>>
>> I wasn't at this actual F2F meeting so I don't know many details.
>> Brendan might remember what the blocking issue was?
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20131014/30fab54a/attachment.html>


More information about the es-discuss mailing list