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 <bren...@mozilla.com> wrote: > Benjamin (Inglor) Gruenbaum <mailto:ing...@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.arvids...@gmail.com <mailto: >> erik.arvidsson@gmail.**com <erik.arvids...@gmail.com>>> >> To: Brendan Eich <bren...@mozilla.com <mailto:bren...@mozilla.com>> >> Cc: es-discuss <es-discuss@mozilla.org <mailto:es-discuss@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? >> >
_______________________________________________ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss