Scoped binding of a method to an object

# Benjamin (Inglor) Gruenbaum (11 years ago)

Scoped binding of a method to an object

Well, I know how some languages solve this issue but I wondered if ECMAScript considered addressing this or already have and I missed it.

Often, I want to extend objects, for example -

Array.prototype.shuffle - takes an array and shuffles it.

However, this is considered bad practice for many reasons I don't have to repeat here (what if other libraries also override it? What if some user code? What if it makes it to the standard some day?)

The problem is even more prevalent in stuff like String.prototype.contains which we know will be in the next spec - people had little way to add that function to the string prototype before it was adapted to the spec and not get eventually bitten.

Stuff like underscore _.shuffle makes for less object oriented code, I find it harder to write and it feels like the code is not where it belongs.

What I'd like is a way to add such methods in a scoped way, so within my code I get to use .shuffle but any code that is not in that scoped block does not get access to this method.

Has there been a proposal or discussion about this in the past?

# Rick Waldron (11 years ago)

This is trivial with Symbols:

let shuffle = Symbol();

Array.prototype[shuffle] = function() {...};

Only code that has access to the shuffle symbol may use the method:

let shuffled = array[shuffle]();
# Kevin Smith (11 years ago)

This is trivial with Symbols:

Unfortunately, such a construction will fail in a multi-realm (i.e. multiple frames) environment.

# Till Schneidereit (11 years ago)

That's what the symbol registry will solve, though.

# Brendan Eich (11 years ago)

Benjamin (Inglor) Gruenbaum wrote:

However, this is considered bad practice for many reasons I don't have to repeat here (what if other libraries also override it? What if some user code? What if it makes it to the standard some day?)

Actually, people say extending Object.prototype is bad practive (verboten, the original post, IIRC from Arv, said; yes, I recalled correctly: erik.eae.net/archives/2005/06/06/22.13.54).

Extending Array.prototype is less so, and PrototypeJS did it. Other libraries extend built-in prototypes. Some even take care not to jump a prior claim.

The problem is even more prevalent in stuff like String.prototype.contains which we know will be in the next spec - people had little way to add that function to the string prototype /before/ it was adapted to the spec and not get eventually bitten.

No, object detection, polyfilling, and even "prollyfilling" are common and successful adaptationsp on the Web.

Symbols help but (without dot syntax) also hurt. We'll see how much use they get in the future, but for the record, string-equated names have been and will be used to extend standard built-ins too.

Your subject recalls a defunct proposal to add lexically-scoped but heap-based -- therefore object property-lookup performance hindering

# Brendan Eich (11 years ago)

Apologies for the (iPad + haste)-induced typos below :-(.

# Erik Arvidsson (11 years ago)

We did proposes this back in 2011

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?

# Till Schneidereit (11 years ago)

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

Benjamin (Inglor) Gruenbaum wrote:

However, this is considered bad practice for many reasons I don't have to repeat here (what if other libraries also override it? What if some user code? What if it makes it to the standard some day?)

Actually, people say extending Object.prototype is bad practive (verboten, the original post, IIRC from Arv, said; yes, I recalled correctly: erik.eae.net/archives/2005/06/06/22.13.54).

Extending Array.prototype is less so, and PrototypeJS did it. Other libraries extend built-in prototypes. Some even take care not to jump a prior claim.

Given the hoops we have to jump through because of Array.prototype and String#prototype extensions right now (1 and Unscopable), I would argue that extending any builtins' prototypes without at least some poor-man's namespacing shouldn't be done, either.

I do agree that poly- and prollyfilling can be quite convenient, but ISTM that the problems they create for the language's builtins library ability to evolve only increase over time.

If we ever manage to introduce better syntax for by-symbol property lookups, that problem would be thoroughly solved.

# Brendan Eich (11 years ago)

Erik Arvidsson <mailto:erik.arvidsson at gmail.com> October 13, 2013 10:32 AM We did proposes this back in 2011

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?

I wrote why in my reply, cited below:

Your subject recalls a defunct proposal to add lexically-scoped but heap-based -- therefore object property-lookup performance hindering -- extension properties. This proposal died precise because of the performance problem.

Every property access sprouts a third parameter beyond object and property name, namely a lexical scope token of some kind. All property maps in objects shared in the heap also sprout such a scope token along with property name.

(This is quite reminiscent of ES4 namespaces, which we agreed to reject from any future ECMA-262 in order to forge Harmony in 2008. See esdiscuss/2008-August/006837.html.)

Implementors objected, including V8 folks (if I recall correctly, Andreas Rossberg). This was at the May 2011 TC39 meeting hosted at the University of California at Santa Cruz.

# David Bruant (11 years ago)

Le 13/10/2013 19:44, Till Schneidereit a écrit :

Given the hoops we have to jump through because of Array.prototype and String#prototype extensions right now ([1] and Unscopable), I would argue that extending any builtins' prototypes without at least some poor-man's namespacing shouldn't be done, either.

I do agree that poly- and prollyfilling can be quite convenient, but ISTM that the problems they create for the language's builtins library ability to evolve only increase over time.

Unless an agreement is found between the platform and authors on, for instance, a part of the namespace that'd be exclusively reserved to authors: lists.w3.org/Archives/Public/public-script-coord/2013JulSep/0430.html (the wording is a bit too strong and some minor adjustements followed in posts, but this posts explains the idea well enough)

Concretely, attempted prolyfills, could be _-prefixed (that really fits with what you call "poor-man's prefixing", I believe) Authors would feel free to add something like Array.prototype._shuffle or Array.prototype._last, or EventTarget.prototype._on without worrying about collision with the platform. We need agreement from the platform though.

... we might not actually need agreement from the platform. Claiming the namespace, shipping a library using it and spreading the word about it on standard mailing-lists could be enough... but somewhat douchebaggy which is why I shied away from doing it so far...

# Benjamin (Inglor) Gruenbaum (11 years ago)

Brendan Eich <brendan at mozilla.com> wrote:

No, object detection, polyfilling, and even "prollyfilling" are common

and successful adaptationsp on the Web.

Polyfilling is great after the method has already been added to the spec. I'm completely fine with adding an Array.prototype.map shim to IE8, the problem with adding a method that's not on the prototype yet is that it'll fail in case the spec is different from the implementation I chose. If you mentioned PrototypeJS, its .bind method is one such example.

Your subject recalls a defunct proposal to add lexically-scoped but

heap-based -- therefore object property-lookup performance hindering -- extension properties.

I have to say this surprises me, a performance issue is the last thing I expected. What about attaching a prototype as a closure variable, something (and this is a syntax I don't like) like:

(function(use Array){
    Array.prototype.contains = function() { ...
    ...
    // any code here has access to .contains, code that did not originate
here does not have such access, much like a closure.
    // other code is free to use Array without any collisions.
})()

Again, I don't like this sort of syntax and I'm not sure about the semantics here, I just noticed I have this problem - I'm probably not the most qualified for coming up with the solution out of the amazing minds right here.

but for the record, string-equated names have been and will be used to

extend standard built-ins too.

I completely agree here. I'm stoked about symbols just like the next guy but using symbols here (with the bracket syntax) seems unnatural and not what I intended to do.

Kevin Smith <zenparsing at gmail.com> wrote:

That's what the symbol registry will solve, though.

That's an interesting approach. However, even if we overlook the (maybe somewhat abusing?) nature of using something like the symbol registry for coordinating global state like this. I think this can appear as somewhat of an overkill for something that sounds to me a common problem and a reasonable use case.

I just thought it would be really nice to have things like NodeList.prototype.sort or Array.prototype.shuffle without worrying about conflicts with other libraries or the language itself in a few years.

# Benjamin (Inglor) Gruenbaum (11 years ago)

David Bruant <bruant.d at gmail.com> wrote:

Concretely, attempted prolyfills, could be _-prefixed (that really fits

with what you call "poor-man's prefixing", I believe)

Authors would feel free to add something like Array.prototype._shuffle or

Array.prototype._last, or EventTarget.prototype._on without worrying about collision with the platform.

What if I use two libraries that polyfill _shuffle or _last differently (let's say with different behavior for an empty array for _last or weaker guarantee on the randomness in _shuffle)?

# Brendan Eich (11 years ago)

Till Schneidereit <mailto:till at tillschneidereit.net> October 13, 2013 10:44 AM On Sun, Oct 13, 2013 at 7:17 PM, Brendan Eich<brendan at mozilla.com> wrote:

Benjamin (Inglor) Gruenbaum wrote:

However, this is considered bad practice for many reasons I don't have to repeat here (what if other libraries also override it? What if some user code? What if it makes it to the standard some day?) Actually, people say extending Object.prototype is bad practive (verboten, the original post, IIRC from Arv, said; yes, I recalled correctly: erik.eae.net/archives/2005/06/06/22.13.54).

Extending Array.prototype is less so, and PrototypeJS did it. Other libraries extend built-in prototypes. Some even take care not to jump a prior claim.

Given the hoops we have to jump through because of Array.prototype and String#prototype extensions right now ([1] and Unscopable), I would argue that extending any builtins' prototypes without at least some poor-man's namespacing shouldn't be done, either.

The problems with those are not definitive enough for library authors to give up usability, from all evidence.

SugarJS from [1] is a case in point. It won not just because it beat TC39 to the punch, but because it could work its will on the mutable built-in prototypes.

The unscopable proposal arose due to 'with'. Many people avoid 'with' (all JSLint users). That's even less definitive an objection.

I do agree that poly- and prollyfilling can be quite convenient, but ISTM that the problems they create for the language's builtins library ability to evolve only increase over time.

Good reason to get TC39 out of the library evolution business, in my view! Github does much better. The standards body can codify de-facto winners.

If we ever manage to introduce better syntax for by-symbol property lookups, that problem would be thoroughly solved.

The challenge is to do so without adding another (even static-only, not reflected in the heap) binding rib.

# Benjamin (Inglor) Gruenbaum (11 years ago)

Keeping a third parameter beyond object and property name seems unnecessary.

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.

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); }

?

# Mark S. Miller (11 years ago)

On Sun, Oct 13, 2013 at 11:03 AM, Benjamin (Inglor) Gruenbaum < inglor at gmail.com> wrote:

David Bruant <bruant.d at gmail.com> wrote:

Concretely, attempted prolyfills, could be _-prefixed (that really fits with what you call "poor-man's prefixing", I believe) Authors would feel free to add something like Array.prototype._shuffle or Array.prototype._last, or EventTarget.prototype._on without worrying about collision with the platform.

What if I use two libraries that polyfill _shuffle or _last differently (let's say with different behavior for an empty array for _last or weaker guarantee on the randomness in _shuffle)?

Whichever first freezes after polyfilling (or otherwise monkey patching) primordials wins.

If you want to do a "scoped" polyfill insensitive to such freezing or conflicting monkey patching, use WeakMaps or (post ES6) relationships.

# David Bruant (11 years ago)

Le 13/10/2013 20:03, Benjamin (Inglor) Gruenbaum a écrit :

David Bruant <bruant.d at gmail.com <mailto:bruant.d at gmail.com>> wrote:

Concretely, attempted prolyfills, could be _-prefixed (that really fits with what you call "poor-man's prefixing", I believe) Authors would feel free to add something like Array.prototype._shuffle or Array.prototype._last, or EventTarget.prototype._on without worrying about collision with the platform.

What if I use two libraries that polyfill _shuffle or _last differently (let's say with different behavior for an empty array for _last or weaker guarantee on the randomness in _shuffle)?

What do you do today when a library overrides Array.prototype.concat with a different semantics? What do you do when you load two libraries each defining a global $ with different semantics?

Apply every prevention/resolution mechanism you use today with global properties. Among other ideas:

  • don't load libraries that are known to have conflicts (!!)
  • isolate the code that enhance built-ins (preferably, run it before anything else and not in the middle of your application lifecycle)
  • Define functions as non-configurable/non-writable in development mode to discover conflicts before pushing to production.

This proposal does not aim at solving the problem of library conflicts which existed before this proposal and should be solve independently.

# Till Schneidereit (11 years ago)

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

Till Schneidereit <mailto:till at tillschneidereit.net> October 13, 2013 10:44 AM

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

Benjamin (Inglor) Gruenbaum wrote:

However, this is considered bad practice for many reasons I don't have to repeat here (what if other libraries also override it? What if some user code? What if it makes it to the standard some day?)

Actually, people say extending Object.prototype is bad practive (verboten, the original post, IIRC from Arv, said; yes, I recalled correctly: erik.eae.net/archives/2005/06/06/22.13.54).

Extending Array.prototype is less so, and PrototypeJS did it. Other libraries extend built-in prototypes. Some even take care not to jump a prior claim.

Given the hoops we have to jump through because of Array.prototype and String#prototype extensions right now ([1] and Unscopable), I would argue that extending any builtins' prototypes without at least some poor-man's namespacing shouldn't be done, either.

The problems with those are not definitive enough for library authors to give up usability, from all evidence.

SugarJS from [1] is a case in point. It won not just because it beat TC39 to the punch, but because it could work its will on the mutable built-in prototypes.

And now it causes problem for TC39, browser vendors, sites that use it and end users. (The last group should be affected the least because the first three groups work together to prevent it, of course.)

The unscopable proposal arose due to 'with'. Many people avoid 'with' (all JSLint users). That's even less definitive an objection.

It's used, though, and will keep being used, I'm afraid.

I do agree that poly- and prollyfilling can be quite convenient, but ISTM that the problems they create for the language's builtins library ability to evolve only increase over time.

Good reason to get TC39 out of the library evolution business, in my view! Github does much better. The standards body can codify de-facto winners.

I wholeheartedly agree on not doing library evolution in TC39. However, GitHub-based evolution doesn't always (or, never does, really) result in wins that are clear-cut enough that codifying them won't break the web. If two heavily-used libraries add the same method with slightly different arguments, that method can't ever be codified.

# Benjamin (Inglor) Gruenbaum (11 years ago)

David Bruant <bruant.d at gmail.com> wrote

This proposal does not aim at solving the problem of library conflicts

which existed before this proposal and should be solve independently.

Of course, and I'm sorry if I implied otherwise. I'm sure we all acknowledge the problem of extending the native prototypes in libraries. I just don't understand how having a _ prefix attempts to give us a solution to the problem. I'm not saying I have a solution either, but I think implying that it's ok to extend them with a prefix doesn't really solve much. Maybe I got what you were trying to say wrong though.

# Mark S. Miller (11 years ago)

Any library that monkey patches primordials and wishes to be able to rely on the primordials being mutated only according to it should demand to be run before primordials are otherwise corrupted or frozen, and should freeze the primordials after patching. Of course, such a "library" isn't a library in the traditional sense -- it is a kernel for everything loaded into that realm after it. Two such kernels aren't composable because of this unsolvable conflict problem.

Libraries meant to be composable should not mutate primordials, and should assume only that primordials are in some uncorrupted state.

# Benjamin (Inglor) Gruenbaum (11 years ago)

First of all - well put.

Second, wouldn't being able to do this in a scoped way solve the conflict problem?

# Brendan Eich (11 years ago)

Benjamin (Inglor) Gruenbaum <mailto:inglor at gmail.com> October 13, 2013 11:00 AM Brendan Eich<brendan at mozilla.com <mailto:brendan at mozilla.com>> wrote:

No, object detection, polyfilling, and even "prollyfilling" are common and successful adaptationsp on the Web.

Polyfilling is great after the method has already been added to the spec.

Prollyfilling is great too (perhaps you disagree?), and as its name suggests it happens before the method is added to the spec.

I'm completely fine with adding an Array.prototype.map shim to IE8, the problem with adding a method that's not on the prototype yet is that it'll fail in case the spec is different from the implementation I chose. If you mentioned PrototypeJS, its .bind method is one such example.

How exactly did that example "fail"? The differences in detail hardly matter; PrototypeJS trod a cow-path that ES5 paved. Most users don't worry about the edge case differences.

Your subject recalls a defunct proposal to add lexically-scoped but heap-based -- therefore object property-lookup performance hindering -- extension properties.

I have to say this surprises me, a performance issue is the last thing I expected. What about attaching a prototype as a closure variable, something (and this is a syntax I don't like) like:

(function(use Array){
    Array.prototype.contains = function() { ...
    ...
    // any code here has access to .contains, code that did not 
originate here does not have such access, much like a closure.
    // other code is free to use Array without any collisions.
})()

Again, I don't like this sort of syntax and I'm not sure about the semantics here, I just noticed I have this problem - I'm probably not the most qualified for coming up with the solution out of the amazing minds right here.

Don't worry about syntax yet. The issue in any such semantic extension is the required extra lookup parameter: not just contains on the right of dot, but the enclosing closure scope.

Unqualified identifiers (ones not used after dot) indeed require lookup via closure environments, to the global (let's ignore 'with' and DOM inline event handlers).

Dot-qualified identifiers and equivalent bracketed computed property name accesses require prototype chain lookup.

No matter the syntax, and independent of details of how one specs it, you're proposing a hybrid of the two schemes. This must cost, and it does cost.

# Brendan Eich (11 years ago)

Adding a prefixing convention is a sure-fire loser, in my experience

# Brendan Eich (11 years ago)

Till Schneidereit <mailto:till at tillschneidereit.net> October 13, 2013 11:28 AM And now it causes problem for TC39, browser vendors, sites that use it and end users. (The last group should be affected the least because the first three groups work together to prevent it, of course.)

I think we agree. My point was that the objections are not definitive, not axiomatic enough. The costs are born "later" and "by others". This is a recipe for social ills.

What we do about it is up to us, but just asserting that developers should stop extending primordials sounds like King Canute to me.

# David Bruant (11 years ago)

Le 13/10/2013 20:29, Benjamin (Inglor) Gruenbaum a écrit :

David Bruant <bruant.d at gmail.com <mailto:bruant.d at gmail.com>> wrote

This proposal does not aim at solving the problem of library conflicts which existed before this proposal and should be solve independently.

Of course, and I'm sorry if I implied otherwise. I'm sure we all acknowledge the problem of extending the native prototypes in libraries. I just don't understand how having a _ prefix attempts to give us a solution to the problem. I'm not saying I have a solution either, but I think implying that it's ok to extend them with a prefix doesn't really solve much. Maybe I got what you were trying to say wrong though.

Not wrong, but partial ;-) '_'-prefixing isn't a solution in itself. It requires a commitment from the platform to say they won't make use of this namespace in the future, thus allowing authors to use this namespace without worrying about future conflicts.

Just to clarify a point, I started speaking about _-prefixing after Till's point about "the language's builtins library ability to evolve". My point is somewhat unrelated to scoped binding and is not a solution for that (I should fork the thread if we continue discussing this).

# Benjamin (Inglor) Gruenbaum (11 years ago)

Prollyfilling is great too (perhaps you disagree?), and as its name

suggests it happens before the method is added to the spec.

So in your opinion adding a .shuffle method to an Array in a library I ship to users is a good idea if it makes my code clearer and nicer?

I'm not saying I disagree, I'm not one of those people who passes an extra parameter for undefined in a function. I just think it can be very risky.

When I have to work on big code bases that run JavaScript end to end using multiple frameworks and libraries on both sides, I find that extending object prototypes can be very risky - and "Prollyfilling" (nice term by the way) has bitten me before. Sure, in my code it can be very nice, but I expect libraries to have little to no side effects on my code and that's exactly the cases it can bite me.

How exactly did that example "fail"? The differences in detail hardly

matter; PrototypeJS trod a cow-path that ES5 paved. Most users don't worry about the edge case differences.

I don't think anyone today is doubting the enormous contribution PrototypeJS had and .bind was very useful in it and is very useful in ES5. However, running into edge cases and getting unexpected results is something I don't think people appreciate. The SweetJS case is just as interesting.

There is no doubt some of the most interesting API additions have come from people extending natives and that says something. However, today when rather than writing 1000 line pages I see more and more 100 thousand lines code bases using multiple libraries - side effects that extending natives sound very scary, and being able to extend the natives in a way that does not bite me is something I'd really love to play with.

Don't worry about syntax yet.

Of course, I was just toying around. Like I've said before, pretty much anyone else in this discussion is more qualified to come with an actual solution than me.

This must cost, and it does cost.

Would you mind elaborating on that or linking to relevant discussion about the last proposal? I'd love to understand the issues involved and get a better understanding of the challenges something like this would bring.

# Brian Kardell (11 years ago)

On Oct 13, 2013 2:01 PM, "Benjamin (Inglor) Gruenbaum" <inglor at gmail.com>

wrote:

Brendan Eich <brendan at mozilla.com> wrote:

No, object detection, polyfilling, and even "prollyfilling" are common

and successful adaptationsp on the Web.

Polyfilling is great after the method has already been added to the

spec. I'm completely fine with adding an Array.prototype.map shim to IE8, the problem with adding a method that's not on the prototype yet is that it'll fail in case the spec is different from the implementation I chose. If you mentioned PrototypeJS, its .bind method is one such example.

Note the R though - see prollyfill. org and search public-nextweb for "prefix" to find a number of debates on this very topic. I too would like to see a lot of smart folks on the various lists agree to some kind of best practice on this so we can keep things forward compatible and avoid situations that potentially break as something moves toward standard - that bit us a lot historically.

# Mark S. Miller (11 years ago)

On Sun, Oct 13, 2013 at 11:36 AM, Benjamin (Inglor) Gruenbaum < inglor at gmail.com> wrote:

First of all - well put.

Thanks.

Second, wouldn't being able to do this in a scoped way solve the conflict problem?

As Brendan points out, doing this in a "scoped" way adds a third parameter representing the scope, or that somehow needs to be scope specific. Such a third parameter needs to be first class. WeakMaps and relationships do exactly that.

# Benjamin (Inglor) Gruenbaum (11 years ago)

As Brendan points out, doing this in a "scoped" way adds a third parameter

representing the scope, or that somehow needs to be scope specific. Such a third parameter needs to be first class. WeakMaps and relationships do exactly that.

My relative lack of experience here is probably working against me.

Would you mind explaining how WeakMaps (and later relationships) give me that third scope parameter? Also, are you implying adding this sort of scoped extension of a native prototype be possible/easy once those features are implemented?

(Sending me to reading is great too, I don't want to waste your time)

# Erik Arvidsson (11 years ago)

Let's not kid ourselves. WeakMaps have bad user ergonomics and runtime ergonomics. I think at this point non method functions are much better.

# Brendan Eich (11 years ago)

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.

# Brendan Eich (11 years ago)

Erik Arvidsson wrote:

Let's not kid ourselves. WeakMaps have bad user ergonomics and runtime ergonomics. I think at this point non method functions are much better.

Hang on, relationships and new syntax can solve both performance and ergonomics concerns, we think.

Perhaps you missed the March meeting where Mark discussed relationships (big meeting, it may have been near or even over a break).

The key insight is that with:

obj at rel obj at rel = val

we can avoid the "dot dilemma" of having to parameterize by scope, instead treat these two as

rel.@@get(obj) rel.@@set(obj, val)

respectively, under the hood.

Mark observed further that where rel could be a weak map, it should not be implemented as an ephemeron table with all the GC costs that entails. Because such an "object field" relationship outlives all object instances, it is equivalent to a class or trait private member, and the value can be stored directly on the object.

The issue of proxy leaking is dodged by delegating to rel, which can itself by proxied in a membrane setting, IIRC.

But at this point I should summon Mark to take over. My point is that it seems not everyone heard Mark's statement that weakmaps should not be used to implement relationships except in preprocessors targeting ES6 (assuming we get relationships into ES7).

# Erik Arvidsson (11 years ago)

On Oct 13, 2013 3:23 PM, "Brendan Eich" <brendan at mozilla.com> wrote:

Erik Arvidsson wrote:

Let's not kid ourselves. WeakMaps have bad user ergonomics and runtime

ergonomics. I think at this point non method functions are much better.

Hang on, relationships and new syntax can solve both performance and

ergonomics concerns, we think.

Perhaps you missed the March meeting where Mark discussed relationships (big meeting, it may have been near or even over a break).

I did not miss it.

The key insight is that with:

obj at rel obj at rel = val

we can avoid the "dot dilemma" of having to parameterize by scope,

instead treat these two as

rel.@@get(obj) rel.@@set(obj, val)

respectively, under the hood.

Mark observed further that where rel could be a weak map, it should not

be implemented as an ephemeron table with all the GC costs that entails. Because such an "object field" relationship outlives all object instances, it is equivalent to a class or trait private member, and the value can be stored directly on the object.

Using a WeakMap for rel is a bit too simplistic. We need to read and write through to the prototype but you are right that rel can be implemented using WeakMaps.

The issue of proxy leaking is dodged by delegating to rel, which can

itself by proxied in a membrane setting, IIRC.

But at this point I should summon Mark to take over. My point is that it

seems not everyone heard Mark's statement that weakmaps should not be used to implement relationships except in preprocessors targeting ES6 (assuming we get relationships into ES7).

Exactly.

# Rick Waldron (11 years ago)

On Sun, Oct 13, 2013 at 12:45 PM, Kevin Smith <zenparsing at gmail.com> wrote:

This is trivial with Symbols:

let shuffle = Symbol();

Array.prototype[shuffle] = function() {...};

Only code that has access to the shuffle symbol may use the method:

let shuffled = arrayshuffle;

Unfortunately, such a construction will fail in a multi-realm (i.e. multiple frames) environment.

Not if I register the symbol in the not-yet-specified symbol registry.

# Brendan Eich (11 years ago)

Erik Arvidsson wrote:

... My point is that it seems not everyone heard Mark's statement that weakmaps should not be used to implement relationships except in preprocessors targeting ES6 (assuming we get relationships into ES7).

Exactly.

Ok, that's what I was hoping for. We need Traceur, not poorly-performing and unergonomic by-hand codings.

To borrow from Dean Wormer: slow, hard to read, and hard to write is no way to go through life! ;-)

# Rick Waldron (11 years ago)

On Sun, Oct 13, 2013 at 2:00 PM, Benjamin (Inglor) Gruenbaum < inglor at gmail.com> wrote:

Brendan Eich <brendan at mozilla.com> wrote:

No, object detection, polyfilling, and even "prollyfilling" are common and successful adaptationsp on the Web.

Polyfilling is great after the method has already been added to the spec. I'm completely fine with adding an Array.prototype.map shim to IE8, the problem with adding a method that's not on the prototype yet is that it'll fail in case the spec is different from the implementation I chose. If you mentioned PrototypeJS, its .bind method is one such example.

Your subject recalls a defunct proposal to add lexically-scoped but heap-based -- therefore object property-lookup performance hindering -- extension properties.

I have to say this surprises me, a performance issue is the last thing I expected. What about attaching a prototype as a closure variable, something (and this is a syntax I don't like) like:

(function(use Array){
    Array.prototype.contains = function() { ...
    ...
    // any code here has access to .contains, code that did not originate
here does not have such access, much like a closure.
    // other code is free to use Array without any collisions.
})()

Another solution is creating your own subclass with custom methods...

class XArray extends Array { constructor(...args) { super(...args); } contains(arg) { ... } }

Then all code in this application uses XArray and gets its custom "contains"

# Peter Seliger (11 years ago)

If you really have to live with those restriction you do describe, there still is a low level solution working since ES 3 that keeps the implemented codebase of certain behaviors at one place without effecting any other code - function based traits and mixins.

One could e.g. write ones own implementations of an Enumerable's [first], [last] accessors.

var Enumerable_first_last = (function () {

var Trait,

first = function () {
  return this[0];
},
last = function () {
  return this[this.length - 1];
}

;

Trait = function () {

this.first = first;
this.last = last;

};

return Trait;

}());

From that point one is free to decide of where to apply that trait.

var str = "JavaScript natively supports Traits", arr = str.split(" "), coll = { "0": "foo", "1": "bar", "2": "baz", "length": 2 } ;

It is totally fine to apply additional behavior separately to objects that are in need of it.

Enumerable_first_last.call(arr);

arr.first(); // "JavaScript" arr.last(); // "Traits"

Enumerable_first_last.call(coll);

coll.first(); // "foo" coll.last(); // "bar"

One also could delegate this behavior to an objects prototype.

Enumerable_first_last.call(String.prototype); // works for all [[String]] implementations that do allow access via [idx]

str.first(); // "J" str.last(); // "s"

This approach in its first step decouples implementation from having it directly effecting a system. The second step is about responsibility and making decisions.

Peter

# Benjamin (Inglor) Gruenbaum (11 years ago)

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.

# Benjamin (Inglor) Gruenbaum (11 years ago)

Function traits and mixins are the bread and butter of JS. Being able to share functionality is the pumping heart of prototypical inheritance, especially in a behaviorally typed environment.

Much like a possible issue I have with what Rick suggested (although mixins and decoration are not the same) - let's say you have Enumerable_first_last.call(arr) , and then I .map or .filter that mixed in array - the resulting array no longer has the trait.

I can of course call Object.mixin on the result of .map and override .map on a Enumerable_first_last but that's a tedious process I don't want to have to do for every trait (what if I add several? Do I now have to keep track and compose?)

It's an interesting suggestion and a common pattern, but I really hope we can do better.

# Till Schneidereit (11 years ago)

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

Till Schneidereit <mailto:till at tillschneidereit.net> October 13, 2013 11:28 AM

And now it causes problem for TC39, browser vendors, sites that use it and end users. (The last group should be affected the least because the first three groups work together to prevent it, of course.)

I think we agree. My point was that the objections are not definitive, not axiomatic enough. The costs are born "later" and "by others". This is a recipe for social ills.

Ok, we do agree, then.

What we do about it is up to us, but just asserting that developers should stop extending primordials sounds like King Canute to me.

Also agreed.

It's still not entirely obvious to me what exactly we should do about it, sadly. And not only now, but going forward. Cow path paving requires the cows' ability to trod on the same ground that might be paved later. That very fact means that the cow paths might end up colliding with paved streets. I don't see how this basic conundrum can be resolved without a scope based resolution-mechanism (no pun intended).

# Benjamin (Inglor) Gruenbaum (11 years ago)

This sort of decoration is nice and it's how I usually do this right now, but this requires me to wrap every array with an XArray call which is tedious, not to mention stuff like .map or .reduce would still return an Array which would result in more endless wrapping.

# Rick Waldron (11 years ago)

On Sunday, October 13, 2013, Benjamin (Inglor) Gruenbaum wrote:

This sort of decoration is nice and it's how I usually do this right now, but this requires me to wrap every array with an XArray call which is tedious,

What do you mean by wrap? Using this looks like:

let values = new XArray(1,2,3,4);

values.contains(3); // this is the custom "contains"

not to mention stuff like .map or .reduce would still return an Array

That's incorrect. XArray.prototype.map would return an XArray instance and reduce always returns what you specify it to return, which can be anything.

# Domenic Denicola (11 years ago)

From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Benjamin (Inglor) Gruenbaum

not to mention stuff like .map or .reduce would still return an Array which would result in more endless wrapping.

This is actually not true as of ES6; ES6 uses this.constructor to figure out how to create the returned instance, which means inherited versions of map/reduce/etc. will automatically return new XArrays.

It's quite a clever trick IMO, so we should all thank Allen for pushing for it and doing the spec work to make it possible :)

# Till Schneidereit (11 years ago)

On Sun, Oct 13, 2013 at 11:50 PM, Domenic Denicola <domenic at domenicdenicola.com> wrote:

From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Benjamin (Inglor) Gruenbaum

not to mention stuff like .map or .reduce would still return an Array which would result in more endless wrapping.

This is actually not true as of ES6; ES6 uses this.constructor to figure out how to create the returned instance, which means inherited versions of map/reduce/etc. will automatically return new XArrays.

It's quite a clever trick IMO, so we should all thank Allen for pushing for it and doing the spec work to make it possible :)

Here's to hoping that it'll actually work in practice and not break too many sites.

# Benjamin (Inglor) Gruenbaum (11 years ago)

Rick Waldron <waldron.rick at gmail.com> wrote:

That's incorrect. XArray.prototype.map would return an XArray instance

and reduce always returns what you specify it to return, which can be anything.

Domenic Denicola <domenic at domenicdenicola.com> wrote:

This is actually not true as of ES6; ES6 uses this.constructor to figure

out how to create the returned instance, which means inherited versions of map/reduce/etc. will automatically return new XArrays.

That actually covers one of the primary use cases I see then. It doesn't solve the problem fully but it does make life a lot easier when trying to extend the natives.

# Brendan Eich (11 years ago)

Benjamin (Inglor) Gruenbaum <mailto:inglor at gmail.com> October 13, 2013 2:28 PM 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?

That's not "dynamic scope", first ("dynamic scope" refers to scoping where an unqualified name's meaning cannot be determined lexically

# Brendan Eich (11 years ago)

Till Schneidereit wrote:

OnSun, Oct 13, 2013 at 11:50 PM, Domenic Denicola <domenic at domenicdenicola.com> wrote:

From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Benjamin (Inglor) Gruenbaum

not to mention stuff like .map or .reduce would still return an Array which would result in more endless wrapping.

This is actually not true as of ES6; ES6 uses this.constructor to figure out how to create the returned instance, which means inherited versions of map/reduce/etc. will automatically return new XArrays.

It's quite a clever trick IMO, so we should all thank Allen for pushing for it and doing the spec work to make it possible:)

Here's to hoping that it'll actually work in practice and not break too many sites.

Yes, we are not nearly out of the woods on this one.

In fact, at the September TC39 meeting, did we not agree that more work was needed, and to go back to the lab? Allen (editor) and Rick (notes taker) know.

# Benjamin (Inglor) Gruenbaum (11 years ago)

Yeah, I don't see how it's going to happen either. Now that I get you meant dynamic as in contrary to lexical scoping having scoped object extensions seems very counter intuitive - especially given the direction the language is heading.

Thanks for the discussion.

# Rick Waldron (11 years ago)

On Sunday, October 13, 2013, Brendan Eich wrote:

Till Schneidereit wrote:

OnSun, Oct 13, 2013 at 11:50 PM, Domenic Denicola <domenic at domenicdenicola.com> wrote:

From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Benjamin (Inglor) Gruenbaum

not to mention stuff like .map or .reduce would still return an Array which would result in more endless wrapping.

This is actually not true as of ES6; ES6 uses this.constructor to figure out how to create the returned instance, which means inherited versions of map/reduce/etc. will automatically return new XArrays.

It's quite a clever trick IMO, so we should all thank Allen for pushing for it and doing the spec work to make it possible:)

Here's to hoping that it'll actually work in practice and not break too many sites.

Yes, we are not nearly out of the woods on this one.

In fact, at the September TC39 meeting, did we not agree that more work was needed, and to go back to the lab? Allen (editor) and Rick (notes taker) know.

The work that remained was wrapped up in draft 19, published sept 27th. Allen should confirm (as always)

# Andreas Rossberg (11 years ago)

On 13 October 2013 19:49, Brendan Eich <brendan at mozilla.com> wrote:

Erik Arvidsson <mailto:erik.arvidsson at gmail.com> October 13, 2013 10:32 AM

We did proposes this back in 2011

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?

I wrote why in my reply, cited below:

Your subject recalls a defunct proposal to add lexically-scoped but heap-based -- therefore object property-lookup performance hindering -- extension properties. This proposal died precise because of the performance problem.

Every property access sprouts a third parameter beyond object and property name, namely a lexical scope token of some kind. All property maps in objects shared in the heap also sprout such a scope token along with property name.

(This is quite reminiscent of ES4 namespaces, which we agreed to reject from any future ECMA-262 in order to forge Harmony in 2008. See esdiscuss/2008-August/006837.html.)

Implementors objected, including V8 folks (if I recall correctly, Andreas Rossberg). This was at the May 2011 TC39 meeting hosted at the University of California at Santa Cruz.

I wasn't at that meeting, but yes, the V8 team strongly objected to this. A scope-dependent, 2-dimensional lookup matrix (even 3-dimensional for variables inside 'with') would be an utter nightmare in terms of complexity and optimisation (even by JS VM standards). Also, the concrete proposal had questionable semantics. Fixing them properly would indeed have brought us back full circle to ES4-style namespaces, as Waldemar argued convincingly at some point.

My take-away was that scoped extension methods are only bearable in a language with a static, nominal class system (like C#), where the additional lookup dimension can be resolved at compile time.

# Brendan Eich (11 years ago)

Andreas Rossberg wrote:

My take-away was that scoped extension methods are only bearable in a language with a static, nominal class system (like C#), where the additional lookup dimension can be resolved at compile time.

Right.

The scg.unibe.ch/archive/papers/Berg03aClassboxes.pdf work, which inspired Ruby refinements as well as the scoped object extensions strawman, is about Smalltalk. For all its dynamic typing, Smalltalk has nominal class types and static method membership judgements.

Based on the research, it seems to me that if we want this in JS, we should look at how to build it on ES6 classes + modules.

# Brendan Eich (11 years ago)

Till Schneidereit <mailto:till at tillschneidereit.net> October 13, 2013 2:39 PM On Sun, Oct 13, 2013 at 8:40 PM, Brendan Eich<brendan at mozilla.com> wrote:

Till Schneidereit<mailto:till at tillschneidereit.net> October 13, 2013 11:28 AM

And now it causes problem for TC39, browser vendors, sites that use it and end users. (The last group should be affected the least because the first three groups work together to prevent it, of course.) I think we agree. My point was that the objections are not definitive, not axiomatic enough. The costs are born "later" and "by others". This is a recipe for social ills.

Ok, we do agree, then.

This kind of problem is also part of life as we know it, in many domains. I don't have a general solution :-).

What we do about it is up to us, but just asserting that developers should stop extending primordials sounds like King Canute to me.

Also agreed.

It's still not entirely obvious to me what exactly we should do about it, sadly. And not only now, but going forward. Cow path paving requires the cows' ability to trod on the same ground that might be paved later. That very fact means that the cow paths might end up colliding with paved streets.

Have you ever driven in town in Boston, Massachusetts, USA? :-P

I don't see how this basic conundrum can be resolved without a scope based resolution-mechanism (no pun intended).

If namespaces and scoped object extensions have fallen, what is left? I agree with Andreas Rossberg, who just wrote

"My take-away was that scoped extension methods are only bearable in a language with a static, nominal class system (like C#), where the additional lookup dimension can be resolved at compile time."

So, see the scg.unibe.ch/archive/papers/Berg03aClassboxes.pdf work, which inspired Ruby refinements as well as the scoped object extensions strawman, and try to come up with compile-time-complete resolution.

# John Lenz (11 years ago)

Does this performance hit still exist in light of Symbol? It seems you could build lexical extensions on top of it without introducing a performance penalty.

# Allen Wirfs-Brock (11 years ago)

On Oct 14, 2013, at 8:42 AM, Brendan Eich wrote:

Andreas Rossberg wrote:

My take-away was that scoped extension methods are only bearable in a language with a static, nominal class system (like C#), where the additional lookup dimension can be resolved at compile time.

Right.

The scg.unibe.ch/archive/papers/Berg03aClassboxes.pdf work, which inspired Ruby refinements as well as the scoped object extensions strawman, is about Smalltalk. For all its dynamic typing, Smalltalk has nominal class types and static method membership judgements.

Based on the research, it seems to me that if we want this in JS, we should look at how to build it on ES6 classes + modules.

I'm not sure I buy your "Smalltalk has nominal class types" assertion, but Smalltalk does generally process class definitions as a complete unit and certain dynamic changes to a class definition can cause the complete reprocessing (eg, recompiling methods from source) of a class and all of its subclasses (and the dynamic mutation of all existing instances of those classes!) to match the revised definition. The reprocessing can also fail if the change introduces any inconsistencies such as deleting instances variables that are still referenced by methods.

I believe that classboxes depends upon this reprocessing step to maintain its invariants.

# Brendan Eich (11 years ago)

Allen Wirfs-Brock <mailto:allen at wirfs-brock.com> October 14, 2013 10:52 AM

I'm not sure I buy your "Smalltalk has nominal class types" assertion,

I defer to your Smalltalk expertise :-P. However, there's nothing I know of that allows unrelated class definitions to be related by the subclass relation. Right?

In ES6, due to Object.setPrototypeOf/proto, two unrelated classes can become related in a dynamic fashion.

but Smalltalk does generally process class definitions as a complete unit and certain dynamic changes to a class definition can cause the complete reprocessing (eg, recompiling methods from source) of a class and all of its subclasses (and the dynamic mutation of all existing instances of those classes!) to match the revised definition. The reprocessing can also fail if the change introduces any inconsistencies such as deleting instances variables that are still referenced by methods.

I believe that classboxes depends upon this reprocessing step to maintain its invariants.

My point!

# Allen Wirfs-Brock (11 years ago)

On Oct 14, 2013, at 11:06 AM, Brendan Eich wrote:

Allen Wirfs-Brock <mailto:allen at wirfs-brock.com> October 14, 2013 10:52 AM

I'm not sure I buy your "Smalltalk has nominal class types" assertion,

I defer to your Smalltalk expertise :-P. However, there's nothing I know of that allows unrelated class definitions to be related by the subclass relation. Right?

In ES6, due to Object.setPrototypeOf/proto, two unrelated classes can become related in a dynamic fashion.

You can dynamically change the superclass of a class but that will trigger the recompilation process for that class and its subclasses (implies that superclass to subclass links must be available). This is mostly about the instance variable (ie, private state) shape of the instances and methods that reference instance variable. Methods lookups are still dynamic which is the reason I think "nominal class types" a la Java or C# doesn't really apply.

# Brendan Eich (11 years ago)

John Lenz <mailto:concavelenz at gmail.com> October 14, 2013 9:59 AM

Does this performance hit still exist in light of Symbol?

Yes. Symbol is just an alternative property name type. Think of it in pseudo-ML:

type PropertyName = String | Symbol

where of course practical engines optimize further by recognizing index names (strings that contain integers within certain bounds) and representing them as boxed or tagged machine integers.

Symbols do not add a third parameter (scope token, namespace qualifier, Common Lisp symbol package).

It seems you could build lexical extensions on top of it without introducing a performance penalty.

Something like this was proposed in the early days of symbols, then called names (also "private names"):

doku.php?id=strawman:names&rev=1283890104#binding_private_names

People objected (Andrew Dupont started the ball rolling, see esdiscuss/2011-March/013233 and read the whole thread) to adding a static (compile-time-only) after-dot-in-property-expression/after-colon-in-object-literal name lookup chain.

The objection has a specific and a general aspect:

Specific: once a private foo; declaration is in scope, especially for a large scope, it is hard to avoid errors reusing the name (or using it on unrelated objects) without the private binding tainting all after-dot/colon uses.

General: JS users are not used to thinking about the name after the dot as being looked up in any chain other than the prototype chain. Adding a lookup chain, even if only compile-time, will break users' brains enough to add significant bug habitat.

I'm not sure how to overcome these, but I bet that if there's a way, then classes play a part. Within the scope of a class body one is most likely to want something like private names in scope. Indeed Oliver Hunt and others have wished for this, but it does not work for class-private instance variables in dynamically typed JS:

class Point { private x, y; // made-up syntax, not the point so carry on... ... add(other) { return Point(this.x + other.x, this.y + other.y); } }

We require this.x, not just x, for the instance variable, because we must be able to express other.x as well.

The alternative more recently discussed for relationships would use @ not dot as the connective when denoting private members. That avoids the issue for private name uses alongside public 'x' and 'y', but does not relieve us from requiring private declarations of some kind. And @ does not help with classboxes or scoped object extensions.

So let's set aside @ for the moment. As shown above, if we had something like private name binding declarations, then the use of 'x' or 'y' inside the body after dot-in-property-expression or colon-in-object-literal would always denote the private names, not the string-equated namest.

If this is tolerable (modulo how the design issues around declaring private x and y), then perhaps classboxes in JS can be built similarly.

Lot of "ifs" here.

# Brendan Eich (11 years ago)

Allen Wirfs-Brock <mailto:allen at wirfs-brock.com> October 14, 2013 11:19 AM

You can dynamically change the superclass of a class but that will trigger the recompilation process for that class and its subclasses (implies that superclass to subclass links must be available). This is mostly about the instance variable (ie, private state) shape of the instances and methods that reference instance variable.

This is metaprogramming magic I never learned (my Smalltalk experience was brief: Byte mag, homebrew Pascal impl I never finished, then the real deal

# Benjamin (Inglor) Gruenbaum (11 years ago)

On Mon, Oct 14, 2013 at 6:44 PM, Brendan Eich <brendan at mozilla.com> wrote:

So, see the scg.unibe.ch/archive/**papers/Berg03aClassboxes.pdfscg.unibe.ch/archive/papers/Berg03aClassboxes.pdf work,

which inspired Ruby refinements as well as the scoped object extensions strawman, and try to come up with compile-time-complete resolution.

Wait a minute, does this mean that the major blocking step in this proposal is the speed?

Are we in agreement that given the performance issue is solved we:

a) Want this sort of feature/ability in the language with the extra syntax it entails at all? b) Want it enough to ask browser vendors actually implement it in their JS engines? c) Want it enough to actually write down a clear scope resolution algorithm that decdes when to choose extension methods?

Because I thought that the problem you've had with it is that it creates more confusing scoping to the user.

# Russell Leggett (11 years ago)

I get that this isn't really the same, but I think one really viable solution for the scoped method problem (which is really just the expression problem, right?) is the proposed bind operator strawman:bind_operator

It doesn't use dots, so it won't mask the difference between the normal prototype chain with some additional scoped binding (for good or ill), but along with it comes the clarity and comfort of lexical binding and also the potential use of the module system.

import {shuffle,each,filter} from "underscore2";

myArray::shuffle();

And if that isn't enough because you need more polymorphic behavior, I think something like Clojure's protocols could be implemented as a library to be used in conjunction.

# Brendan Eich (11 years ago)

Benjamin (Inglor) Gruenbaum <mailto:inglor at gmail.com> October 14, 2013 12:37 PM On Mon, Oct 14, 2013 at 6:44 PM, Brendan Eich <brendan at mozilla.com <mailto:brendan at mozilla.com>> wrote:

So, see the scg.unibe.ch/archive/papers/Berg03aClassboxes.pdf work, which inspired Ruby refinements as well as the scoped object extensions strawman, and try to come up with compile-time-complete resolution.

Wait a minute, does this mean that the /major/ blocking step in this proposal is the speed?

Not just the performance hit, also the complexity, as Andreas and I independently wrote. Extra parameters refract through internal APIs, they impose not only speed costs but also bug habitat and unwanted degrees of freedom in general. They cost.

A compile-time solution will require something new, syntax and semantics, to confine the complexity. Do that, and then let's talk.

Are we in agreement that given the performance issue is solved we:

a) Want this sort of feature/ability in the language with the extra syntax it entails at all?

Andreas also reminded me that the strawman had semantic design issues too. We can dredge those up if need be, but I think not yet.

Not everyone wants "this" in some single, well-designed sense that merely imposes a speed reduction when used. Again, the complexity hits all paths in engines. The slow paths could be reoptimized but the hit is to the entire object model, not just in certain lexical scopes but via the heap, in all objects.

b) Want it enough to ask browser vendors actually implement it in their JS engines?

Again, there is no well-specified "it" yet.

c) Want it enough to actually write down a clear scope resolution algorithm that decdes when to choose extension methods?

Aha, here is the horse that goes in front of the cart.

Do this first, call it step (a), worry about (b) and (c) later.

Thus your "wait a minute" does not make sense. We're waiting for a coherent and contained design, one that doesn't hit object model and engine perf all over the map, and one that hangs together on its own.

Because I thought that the problem you've had with it is that it creates more confusing scoping /to the user./

I said implementors objected. How did you miss that?

But there were design issues too.

And in general, as we discovered with the 'private foo;' part of the original private names proposal, user confusion or complexity remains an objection. See my previous post.

# Brendan Eich (11 years ago)

Russell Leggett <mailto:russell.leggett at gmail.com> October 14, 2013 12:51 PM I get that this isn't really the same, but I think one really viable solution for the scoped method problem (which is really just the expression problem, right?)

The expression problem (en.wikipedia.org/wiki/Expression_problem) is about the cross-cutting nature of extension, and how FP and OOP seem to trade off against one another.

So "scoped object extensions" is not "just the expression problem", but I think it is related as follows:

If we used only functions (possibly with richer dispatch mechanisms, e.g. multimethods), then lexical scope might be enough to extend without collisions. One just rebinds/renames/shadows to compose functions. Or so I think -- CS gurus should school us here.

However, JS has an OO flavor, especially in its built-ins (including the DOM). So users want extensions on the right of dot (and after colon in object literals).

Libraries such as Underscore push the FP side of the coin; SugarJS, Mootools before it, PrototypeJS apart from Object.prototype go the OO way.

is the proposed bind operator strawman:bind_operator

It doesn't use dots, so it won't mask the difference between the normal prototype chain with some additional scoped binding (for good or ill), but along with it comes the clarity and comfort of lexical binding and also the potential use of the module system.

import {shuffle,each,filter} from "underscore2";
myArray::shuffle();

Not bad, and exactly the kind of design I pointed to in the last post or three: new special forms to confine the complexity that killed the SOE strawman.

And if that isn't enough because you need more polymorphic behavior, I think something like Clojure's protocols clojure.org/protocols could be implemented as a library to be used in conjunction.

Clojure wants multimethods too. Some excitement based on my proposal for value object operators.

# Benjamin (Inglor) Gruenbaum (11 years ago)

I think I had a problem articulating my thoughts in this last one.

I was trying not to tie my cart in front of the horse. Even before worrying about implementer performance issues which sound important I wanted to know if:

The problem I had was a real problem to other developers too and it was a big enough problem for people to care about.

I'll gladly try and possibly fail in coming up with a coherent and contained design. I wasn't looking for a solution yet, (and there are probably people here with more clever solutions - but I'll gladly give it my shot) at this stage I'm merely trying to assert if scoped extension methods solve a real problem people have in JavaScript.

I said implementors objected. How did you miss that?

I didn't. I read everything you and other people wrote about it - It was an interesting read too. I'm just not convinced that it's the blocking issue yet.

But there were design issues too. ... user confusion or complexity

remains an objection.

Yes! This is the thing that bothers me most right now about scoped extension methods. Introducing additional syntax to the language seems like a huge objection to me, the fact that it's another thing to teach programmers and another thing to keep in mind when figuring out scopes when reading new code is a big deal in my opinion.

Rick and Domenic pointed out to me that one of the bigger use cases I thought having scoped extension methods solves - extending a native object and not having to re-implement methods seems to be already solved by the clever way in the new spec and is under work. There are still interesting problems I think this solves but I'm really not convinced any more that adding the extra syntax is worth it.

# Brendan Eich (11 years ago)

Definitely deep waters here, not one simple thing. Appreciate your interactions.

Benjamin (Inglor) Gruenbaum wrote:

But there were design issues too. ... user confusion or complexity remains an objection.

Yes! This is the thing that bothers me /most/ right now about scoped extension methods. Introducing additional syntax to the language seems like a huge objection to me, the fact that it's another thing to teach programmers and another thing to keep in mind when figuring out scopes when reading new code is a big deal in my opinion.

Right, that is a cost we always consider. Syntax can't be polyfilled. Once shipped at scale cross-browser, it ends to be "forever".

Rick and Domenic pointed out to me that one of the bigger use cases I thought having scoped extension methods solves - extending a native object and not having to re-implement methods seems to be already solved by the clever way in the new spec and is under work. There are still interesting problems I think this solves but I'm really not convinced any more that adding the extra syntax is worth it.

You mean the use of 'this.constructor' to make generic Array methods that create array results in ES1-5 create appropriate subclass instances?

I still thought that was a breaking change on the web, which we were not sure we could get away with. But I agree with you it helps, a lot. Yet, it doesn't solve the use-cases at which the SOE strawman was aimed.

# Russell Leggett (11 years ago)

On Mon, Oct 14, 2013 at 4:05 PM, Brendan Eich <brendan at mozilla.com> wrote:

Russell Leggett <mailto:russell.leggett at gmail.**com<russell.leggett at gmail.com>

October 14, 2013 12:51 PM

I get that this isn't really the same, but I think one really viable solution for the scoped method problem (which is really just the expression problem, right?)

The expression problem (en.wikipedia.org/wiki/**Expression_problemen.wikipedia.org/wiki/Expression_problem) is about the cross-cutting nature of extension, and how FP and OOP seem to trade off against one another.

So "scoped object extensions" is not "just the expression problem", but I think it is related as follows:

Sorry, I was a bit glib - what I meant was that the ability to add new methods to existing types without modification of the original type and without recompilation is the OO side of it, and that's really what we're talking about here. I brushed with broad strokes, but you clearly see what I mean in your examples. I guess I was trying to invoke all the ideas that have come before in the name of fixing the expression problem and how they would relate to this. I don't actually think SOE is a good fit for p(r)olyfills, which is a different case than what I would really call the expression problem.

If we used only functions (possibly with richer dispatch mechanisms, e.g. multimethods), then lexical scope might be enough to extend without collisions. One just rebinds/renames/shadows to compose functions. Or so I think -- CS gurus should school us here.

However, JS has an OO flavor, especially in its built-ins (including the DOM). So users want extensions on the right of dot (and after colon in object literals).

This is the real trick to why I proposed using the bind operator. The extension methods would take advantage of dynamically bound "this". The methods themselves would be lexically bound - they would simply be variables - however they got there (import, let, destructuring) but the syntactic sugar of :: would allow binding and then calling with a syntax that looks very similar to .

To be clear, underscore methods as written would not work correctly here. Those expect the collection as the first argument. What I was proposing to do with the bind operator would still use "this" to point to the collection (or whatever the intended target), and thus keep the OO feel, it just wouldn't use the dot operator, and wouldn't actually modify the prototype or mess with the lookup chain.

is the proposed bind operator **

doku.php?id=strawman:bind_**operatorstrawman:bind_operator

It doesn't use dots, so it won't mask the difference between the normal prototype chain with some additional scoped binding (for good or ill), but along with it comes the clarity and comfort of lexical binding and also the potential use of the module system.

import {shuffle,each,filter} from "underscore2";
myArray::shuffle();

Not bad, and exactly the kind of design I pointed to in the last post or three: new special forms to confine the complexity that killed the SOE strawman.

And if that isn't enough because you need more polymorphic behavior, I think something like Clojure's protocols clojure.org/protocols could be implemented as a library to be used in conjunction.

Clojure wants multimethods too. Some excitement based on my proposal for value object operators.

Yes, although protocols are able to stick with simple single-dispatch based on the datatype extending the protocol. I think this could potentially work with the trademarks proposalstrawman:trademarks

if that ever happened. But I guess that's probably a different thread.

# Brendan Eich (11 years ago)

Russell Leggett <mailto:russell.leggett at gmail.com> October 14, 2013 2:07 PM

On Mon, Oct 14, 2013 at 4:05 PM, Brendan Eich <brendan at mozilla.com <mailto:brendan at mozilla.com>> wrote:

    Russell Leggett <mailto:russell.leggett at gmail.com
    <mailto:russell.leggett at gmail.com>>
    October 14, 2013 12:51 PM

    I get that this isn't really the same, but I think one really
    viable solution for the scoped method problem (which is really
    just the expression problem, right?)


The expression problem
(http://en.wikipedia.org/wiki/Expression_problem) is about the
cross-cutting nature of extension, and how FP and OOP seem to
trade off against one another.

So "scoped object extensions" is not "just the expression
problem", but I think it is related as follows:

Sorry, I was a bit glib - what I meant was that the ability to add new methods to existing types without modification of the original type and without recompilation is the OO side of it, and that's really what we're talking about here. I brushed with broad strokes, but you clearly see what I mean in your examples. I guess I was trying to invoke all the ideas that have come before in the name of fixing the expression problem and how they would relate to this.

Indeed, the literature is quite rich, and on both FP and OO sides of the fence (yes, there is a fence :-P).

I don't actually think SOE is a good fit for p(r)olyfills, which is a different case than what I would really call the expression problem.

Agreed.

If we used only functions (possibly with richer dispatch
mechanisms, e.g. multimethods), then lexical scope might be enough
to extend without collisions. One just rebinds/renames/shadows to
compose functions. Or so I think -- CS gurus should school us here.

However, JS has an OO flavor, especially in its built-ins
(including the DOM). So users want extensions on the right of dot
(and after colon in object literals).

This is the real trick to why I proposed using the bind operator. The extension methods would take advantage of dynamically bound "this". The methods themselves would be lexically bound - they would simply be variables - however they got there (import, let, destructuring) but the syntactic sugar of :: would allow binding and then calling with a syntax that looks very similar to .

Yes, I got that -- good insight. dherman is championing the bind operator for ES7 still.

To be clear, underscore methods as written would not work correctly here.

Sure, FP-style does not need bind. One point for FP, take that OO! :-D

    And if that isn't enough because you need more polymorphic
    behavior, I think something like Clojure's protocols
    <http://clojure.org/protocols> could be implemented as a
    library to be used in conjunction.


Clojure wants multimethods too. Some excitement based on my
proposal for value object operators.

Yes, although protocols are able to stick with simple single-dispatch based on the datatype extending the protocol. I think this could potentially work with the trademarks proposal strawman:trademarks if that ever happened. But I guess that's probably a different thread.

Definitely. Symbol-named brands may suffice for trademarking, but dispatch via if-else is still a problem.

# Brendan Eich (11 years ago)

Russell Leggett wrote:

It doesn't use dots, so it won't mask the difference between the normal prototype chain with some additional scoped binding (for good or ill), but along with it comes the clarity and comfort of lexical binding and also the potential use of the module system.

import {shuffle,each,filter} from "underscore2";
myArray::shuffle();

This is really winning, thanks again.

We already have good motivation for :: anyway, as sugar for bind. This gives relief to the OO side of the expression problem trade-off by allowing lexical bindings to be composed with method calls -- beautiful. No third scope axis / lookup parameter!

And if that isn't enough because you need more polymorphic behavior, I think something like Clojure's protocols clojure.org/protocols could be implemented as a library to be used in conjunction.

How about a spin-off thread?

# Allen Wirfs-Brock (11 years ago)

On Oct 14, 2013, at 3:58 PM, Brendan Eich wrote:

Russell Leggett wrote:

It doesn't use dots, so it won't mask the difference between the normal prototype chain with some additional scoped binding (for good or ill), but along with it comes the clarity and comfort of lexical binding and also the potential use of the module system.

import {shuffle,each,filter} from "underscore2"; myArray::shuffle();

This is really winning, thanks again.

We already have good motivation for :: anyway, as sugar for bind. This gives relief to the OO side of the expression problem trade-off by allowing lexical bindings to be composed with method calls -- beautiful. No third scope axis / lookup parameter!

Speaking from the perspective of someone whose probably has permanent OO brain damage, it doesn't do a lot for me.

The reason I "invoke a method" on an object is because I want to do polymorphic dispatch on the method name. myArray::shuffle() doesn't do that for me. No polymorphic dispatch. If myArray actually does have a shuffle method it isn't called. If that sort of direct function invocation is what I want, I'll just code a function call. No method invocation syntax and no |this| value is need.

I still don't get why so many JS programmer with a FP orientation want to do things with the |this| binding. |this| is for us OO geeks, if you are doing FP you don't need it. If you want to write shuffle, each, and filter functions just code them as normal functions passing the collection as the first argument. If I want to use you functions as a methods on one of my objects I'll just code something like: class { shuffle() {return shuffle(this)} }

Now, what might be useful would be :: that has approximately this semantics

import {shuffle,each,filter} from "underscore2"; myArray::shuffle();

desugars as

do {let _method = myArray[shuffle.name]; _method ? _method : shuffle}.call(myArray);

in other words, if myArray has a 'shuffle' method, call it; otherwise call the default shuffle method that I'm providing.

# Benjamin (Inglor) Gruenbaum (11 years ago)

Brendan Eich <brendan at mozilla.com> wrote:

We already have good motivation for :: anyway, as sugar for bind. This

gives relief to the OO side of the expression problem trade-off by allowing lexical bindings to be composed with method calls -- beautiful. No third scope axis / lookup parameter!

Yeah, but this doesn't solve the original problem nearly as well IMO since it's suddenly different from a normal method call. Having a different call operator for scoped extension methods or method invocation seems very confusing and counter intuitive for developers.

If I have to remember different invocation mechanics I kind of lost already and I don't have the polymorphism I wanted. I completely agree with Allen here.

Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

Now, what might be useful would be :: that has approximately this

semantics ... _method ? _method : shuffle}.call(myArray);

What if the Array has the method further up the prototype chain - like on Object.prototype and we extend it on Array.prototype? Not to mention that the performance issues that plagued adding it to the normal invocation repeat here.

On Mon, Oct 14, 2013 at 11:14 PM, Brendan Eich <brendan at mozilla.com> wrote:

Right, that is a cost we always consider. Syntax can't be polyfilled.

Once shipped at scale cross-browser, it ends to be "forever".

I've actually went around facebook groups with every day JavaScript developers yesterday. Probably mostly ones that don't know how to read the spec (even the really nice and clear ES5 one), but are working as full time JavaScript developers.

Most of the people I talked to did not seem to appreciate having a breaking syntax change in order to have scoped extension methods. This is still very preliminary (~40 people) but I'd really like to get a stronger sense of what more every day developers think. I'll check around and I'll return with more significant data.

You mean the use of 'this.constructor' to make generic Array methods that

create array results in ES1-5 create appropriate subclass instances?

Yeah, I think that's pretty cool - and I agree that if we can get away with it it's totally worth it :)

# Andreas Rossberg (11 years ago)

On 14 October 2013 22:10, Benjamin (Inglor) Gruenbaum <inglor at gmail.com> wrote:

But there were design issues too. ... user confusion or complexity remains an objection.

Yes! This is the thing that bothers me most right now about scoped extension methods. Introducing additional syntax to the language seems like a huge objection to me, the fact that it's another thing to teach programmers and another thing to keep in mind when figuring out scopes when reading new code is a big deal in my opinion.

Arguably, the syntactic complexity is not huge. It is almost benign, compared to the semantic complexity.

# Benjamin (Inglor) Gruenbaum (11 years ago)

On Tue, Oct 15, 2013 at 12:50 PM, Andreas Rossberg <rossberg at google.com> wrote:

Arguably, the syntactic complexity is not huge. It is almost benign, compared to the semantic complexity.

Agreed. I have a bit of a problem articulating my thoughts clearly through this medium being new. The problem with introducing additional syntax to have scoped extension methods to the language is mostly the semantics scoped methods introduce by introducing the extra type of scope and not the extra rule in the language grammar.

Even the syntax on its own sounds like a huge deal though, it's one more thing to teach every programmer learning the language JavaScript and an extra bit of cognitive overload.

# Andreas Rossberg (11 years ago)

On 15 October 2013 03:09, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

I still don't get why so many JS programmer with a FP orientation want to do things with the |this| binding. |this| is for us OO geeks, if you are doing FP you don't need it.

Well, 'this' comes up because you cannot avoid having to interface with OOish APIs that conflicts with FPish composition patterns. For example, you'd like to be able to do something like

array.map(String.prototype.toLowerCase)

But map expects a function, while toLowerCase is only available as a method. So unless you rewrap all relevant APIs in functional style you have to deal with 'this' in one form or the other, and going back and forth is not pretty with what JS currently offers. (Mind you, a bind operator does not even address this particular example.)

# Mark S. Miller (11 years ago)

On Tue, Oct 15, 2013 at 3:39 AM, Andreas Rossberg <rossberg at google.com>wrote:

On 15 October 2013 03:09, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

I still don't get why so many JS programmer with a FP orientation want to do things with the |this| binding. |this| is for us OO geeks, if you are doing FP you don't need it.

Well, 'this' comes up because you cannot avoid having to interface with OOish APIs that conflicts with FPish composition patterns. For example, you'd like to be able to do something like

array.map(String.prototype.toLowerCase)

Using < conventions:safe_meta_programming>

var bind = Function.prototype.bind;
var uncurryThis = bind.bind(bind.call);

array.map(uncurryThis("".toLowerCase))
# Russell Leggett (11 years ago)

On Tue, Oct 15, 2013 at 3:45 AM, Benjamin (Inglor) Gruenbaum < inglor at gmail.com> wrote:

Brendan Eich <brendan at mozilla.com> wrote:

We already have good motivation for :: anyway, as sugar for bind. This gives relief to the OO side of the expression problem trade-off by allowing lexical bindings to be composed with method calls -- beautiful. No third scope axis / lookup parameter!

Yeah, but this doesn't solve the original problem nearly as well IMO since it's suddenly different from a normal method call. Having a different call operator for scoped extension methods or method invocation seems very confusing and counter intuitive for developers.

If I have to remember different invocation mechanics I kind of lost already and I don't have the polymorphism I wanted. I completely agree with Allen here.

I don't think that the scoped extensions you want are going to happen. I used to like the idea of something like that, but I think it will cause a lot more confusion than its worth. My suggestion for the :: binding operator was basically just trying to leverage an operator that might be coming anyway and giving it more purpose. I mean, heck, you know JavaScript programmers, if :: got added to the language, you know people will use it to make nicer looking (subjectively) APIs in the way I'm describing.

In to polymorphism - its not a polymorphic solution out of the box, or at least - it doesn't provide any affordances for doing polymorphism, but saying that function calls cannot be polymorphic isn't true, we're simply flipping around where the polymorphism happens. Yes, I know this probably sounds like a FP rant, but I think that you can't ignore the FP side of this (invoking the expression problem again here). OO is good at adding new cases to the datatype. Functional is good at adding new functions over the datatype (which is what we're doing here). Trying to get the best of both worlds likely means playing in the middle. I mean, this feels like a "you got chocolate in my peanut butter" "you got peanut butter in may chocolate" moment. You say, "This operator makes my method calls unintuitive because its not a dot. Method calls should consistently use dots." I say, "Variables (including function variables) already have well understood scoping and lookup behavior. The dot operator should have consistent lookup behavior." Is there a way to marry the functional and OO to solve this problem?

If we didn't have :: (which we don't now), I think people will continue to simply use functions like what underscore does. Personally, I'm ok with that. If I want to have unscoped extensions and live with the consequences

  • I will be happy to use symbols. If I want to make a polyfill, I'll just do it the same way we've been doing it. But, as much as Allen seems to accuse me of being an FP guy, I still want to have a thing which feels like a method to be on the right so I find that using :: (if it existed) would be a nice compromise.

Maybe I'll think about what clojure style protocols would look like to answer some of the polymorphism questions.

# Allen Wirfs-Brock (11 years ago)

On Oct 15, 2013, at 6:40 AM, Mark S. Miller wrote:

On Tue, Oct 15, 2013 at 3:39 AM, Andreas Rossberg <rossberg at google.com> wrote: On 15 October 2013 03:09, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

I still don't get why so many JS programmer with a FP orientation want to do things with the |this| binding. |this| is for us OO geeks, if you are doing FP you don't need it.

Well, 'this' comes up because you cannot avoid having to interface with OOish APIs that conflicts with FPish composition patterns. For example, you'd like to be able to do something like

array.map(String.prototype.toLowerCase)

I'd be quite comfortable with saying: array.map(str=>str.toLowerCase())

It preserves the integrity of of the string abstraction rather than just assuming that the naked String.prototype.toLowerCase function is the appropriate one for any particular str value.

Using conventions:safe_meta_programming

var bind = Function.prototype.bind;
var uncurryThis = bind.bind(bind.call);

or, in a slightly less obscure formulation that ignores "safety"):

let uncurryThis = f=>((a0, ...args)=>f.apply(a0, args))

array.map(uncurryThis("".toLowerCase))

although, I still content that

    array.map(str=>str.toLowerCase())

is the better formulation. (and fewer characters for those who worry about such things)

But map expects a function, while toLowerCase is only available as a method. So unless you rewrap all relevant APIs in functional style you have to deal with 'this' in one form or the other, and going back and forth is not pretty with what JS currently offers. (Mind you, a bind operator does not even address this particular example.)

/Andreas

I think arrow functions are the appropriate syntactic convenience and since this started as a discussion of the :: extension we can assume arrows already exist.

We need to know and respect abstraction boundaries. I think careful thought is called for each time you cross the FP/OO boundary. You can't just assume that the a function you extracted from a method property is the right function to use with other objects. You can't just assume that the |this| value of a method corresponds to the first parameter of a function (or visa vera).

Just because we know how an object is build out of functions, you shouldn't routinely take an object abstraction I've created and rip it apart into ins more primitive constituent elements which you then repurpose. To me, this is analogous to talking a function apart into its machine level code sequences and data structures and then doing clever things with them. It's possible, it might even be necessary in some very unusual situation, but it should never be routine.

# Allen Wirfs-Brock (11 years ago)

On Oct 15, 2013, at 7:22 AM, Russell Leggett wrote:

If we didn't have :: (which we don't now), I think people will continue to simply use functions like what underscore does. Personally, I'm ok with that. If I want to have unscoped extensions and live with the consequences - I will be happy to use symbols. If I want to make a polyfill, I'll just do it the same way we've been doing it. But, as much as Allen seems to accuse me of being an FP guy, I still want to have a thing which feels like a method to be on the right so I find that using :: (if it existed) would be a nice compromise.

Hey, it was a general rant and not specifically directed at you or anybody else.

# Mark S. Miller (11 years ago)

On Tue, Oct 15, 2013 at 8:51 AM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

On Oct 15, 2013, at 6:40 AM, Mark S. Miller wrote:

On Tue, Oct 15, 2013 at 3:39 AM, Andreas Rossberg <rossberg at google.com>wrote:

On 15 October 2013 03:09, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

I still don't get why so many JS programmer with a FP orientation want to do things with the |this| binding. |this| is for us OO geeks, if you are doing FP you don't need it.

Well, 'this' comes up because you cannot avoid having to interface with OOish APIs that conflicts with FPish composition patterns. For example, you'd like to be able to do something like

array.map(String.prototype.toLowerCase)

I'd be quite comfortable with saying: array.map(str=>str.toLowerCase())

It preserves the integrity of of the string abstraction rather than just assuming that the naked String.prototype.toLowerCase function is the appropriate one for any particular str value.

Using < conventions:safe_meta_programming>

var bind = Function.prototype.bind;
var uncurryThis = bind.bind(bind.call);

or, in a slightly less obscure formulation that ignores "safety"):

let uncurryThis = f=>((a0, ...args)=>f.apply(a0, args))

array.map(uncurryThis("".toLowerCase))

although, I still content that

    array.map(str=>str.toLowerCase())

is the better formulation. (and fewer characters for those who worry about such things)

It depends on what you're trying to express, as you pointed out earlier. Both address the FP vs OO mismatch -- your's by converting fp to oo, uncurryThis by converting oo to fp. Regarding Allen's question, I suspect your's is the better response ;).

# Russell Leggett (11 years ago)

On Tue, Oct 15, 2013 at 11:59 AM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

On Oct 15, 2013, at 7:22 AM, Russell Leggett wrote:

If we didn't have :: (which we don't now), I think people will continue to simply use functions like what underscore does. Personally, I'm ok with that. If I want to have unscoped extensions and live with the consequences

  • I will be happy to use symbols. If I want to make a polyfill, I'll just do it the same way we've been doing it. But, as much as Allen seems to accuse me of being an FP guy, I still want to have a thing which feels like a method to be on the right so I find that using :: (if it existed) would be a nice compromise.

Hey, it was a general rant and not specifically directed at you or anybody else.

Sure, no offense taken, I just don't put myself in that camp. Even the use of |this| I think is justified as not an FP abuse, I just haven't gotten a chance to flesh out my reasoning yet. Without elaborating too much, (sorry, no time right now), I basically envision the blend of clojure protocols and javascript as basically external mixins. Not just a single function, but potentially a set, and using the |this| for single dispatch the way clojure protocols use the first arg.

# Benjamin (Inglor) Gruenbaum (11 years ago)

First of all interesting analogy and read.

On Tue, Oct 15, 2013 at 5:22 PM, Russell Leggett <russell.leggett at gmail.com> wrote:

I don't think that the scoped extensions you want are going to happen. I

used to like the idea of something like that, but I think it will cause a lot more confusion than its worth.

I'm not convinced either. What I'm going to do for the next couple of days is try to talk to developers about the original problem and see if they even want to solve it. The confusion vs worth is something I'm trying to evaluate. Right now it looks like your estimation is correct but I'm not a fan of partial data.

In a few days when I'll have more concrete data. I'll hopefully be smarter in this regard then.

If we didn't have :: (which we don't now), I think people will continue

to simply use functions like what underscore does. Personally, I'm ok with that

I think using stuff like _.shuffle([1,2,3,4,5]) is not as nice and worse than [1,2,3,4,5].shuffle() . Especially in a more functional interface that does not return the original array (we can even have a generator here).

Let's say I have an array [1,2,3,4,5], I want to filter the odd ones, double the remaining elements and then sum the result. (This is obviously not a real use case, but I think we both know there are such use cases)

The big issue I see here is chaining. _.reduce(_.map(_.filter([1,2,3,4,5],x=>x%2 === 0),x=>2*x),(x,y)=>x+y)

Is a lot less readable than [1,2,3,4,5].filter(x=>x%2===0).map(x=>2*x).reduce((x,y)=>x+y))

The clever .constructor trick when extending Array solves that use case but like Brendan said it doesn't address some of the ones in the old SOE proposal.

Having another invocation syntax like :: sounds like a huge overhead to me.

# Russell Leggett (11 years ago)

If we didn't have :: (which we don't now), I think people will continue to simply use functions like what underscore does. Personally, I'm ok with that

I think using stuff like _.shuffle([1,2,3,4,5]) is not as nice and worse than [1,2,3,4,5].shuffle() . Especially in a more functional interface that does not return the original array (we can even have a generator here).

I prefer it too, which is why I suggested :: - its not as nice in terms of needing to understand a new operator, but it does allow you to think about it in a more OO way, and it allows for forward chaining without wrappers.

Let's say I have an array [1,2,3,4,5], I want to filter the odd ones, double the remaining elements and then sum the result. (This is obviously not a real use case, but I think we both know there are such use cases)

The big issue I see here is chaining. _.reduce(_.map(_.filter([1,2,3,4,5],x=>x%2 === 0),x=>2*x),(x,y)=>x+y) Is a lot less readable than [1,2,3,4,5].filter(x=>x%2===0).map(x=>2*x).reduce((x,y)=>x+y))

Speaking of wrappers - underscore does have the chain method which wraps and allows for chaining. Not ideal, but better, and people are comfortable with it - I mean look at jQuery.

Having another invocation syntax like :: sounds like a huge overhead to me.

Its not an invocation syntax, its fairly trivial binding sugar which can

be used to apply functions to objects as the receiver without modifying the object, which is precisely what you want to be able to do with SOE even if you don't like the syntax.

# Brendan Eich (11 years ago)

Benjamin (Inglor) Gruenbaum <mailto:inglor at gmail.com> October 15, 2013 12:45 AM

Yeah, but this doesn't solve the original problem nearly as well IMO since it's suddenly different from a normal method call. Having a different call operator for scoped extension methods or method invocation seems very confusing and counter intuitive for developers.

That's one-sided, though. The other side you are discounting is the confusion when code in the scope of the extension wants to call cowbow.draw not graphics.draw, but draw has been overridden on the right of dot.

Any solution must allow programmers to say what they mean. Since new syntax is required even in the dot-based proposal (at the end, to declare the extension), the cost of an alternative to dot is not novel in the sense of breaking operation on downrev browsers. In other words, a compiler to older JS will be required in any event.

If I have to remember different invocation mechanics I kind of lost already and I don't have the polymorphism I wanted. I completely agree with Allen here.

Which polymorphism to people want? There is a "DWIM" aspect that cannot possibly cover all uses of, e.g., 'draw' on the right of dot.

# Allen Wirfs-Brock (11 years ago)

On Oct 15, 2013, at 9:44 AM, Benjamin (Inglor) Gruenbaum wrote:

...

The big issue I see here is chaining. _.reduce(_.map(_.filter([1,2,3,4,5],x=>x%2 === 0),x=>2*x),(x,y)=>x+y)
Is a lot less readable than [1,2,3,4,5].filter(x=>x%2===0).map(x=>2*x).reduce((x,y)=>x+y))

first let me do a slight rewrite of the above to eliminate some semantic noise:

import {reduce,map,filter} from "underscore2"; reduce(map(filter([1,2,3,4,5],x=>x%2 === 0),x=>2*x),(x,y)=>x+y)

vs [1,2,3,4,5].filter(x=>x%2===0).map(x=>2*x).reduce((x,y)=>x+y))

Nobody should be thinking of these as equivalent expressions of the same computation. They aren't equivalent because the semantics of the identifiers "reduce", "map", "filter" are quite different in the two formulation.

They mean different things and when express this way it is pretty clear that they are different. I don't see how making them look more similar (while maintaining the semantic difference) would help with code clarity.

# Russell Leggett (11 years ago)

The big issue I see here is chaining. _.reduce(_.map(_.filter([1,2,3,4,5],x=>x%2 === 0),x=>2*x),(x,y)=>x+y) Is a lot less readable than [1,2,3,4,5].filter(x=>x%2===0).map(x=>2*x).reduce((x,y)=>x+y))

P.S. This really doesn't look too shabby to me:

import {reduce,map,filter} from "underscore2";
[1,2,3,4,5]::filter(x=>x%2===0)::map(x=>2*x)::reduce((x,y)=>x+y))
# Brendan Eich (11 years ago)

Allen Wirfs-Brock <mailto:allen at wirfs-brock.com> October 14, 2013 6:09 PM

Speaking from the perspective of someone whose probably has permanent OO brain damage, it doesn't do a lot for me.

The reason I "invoke a method" on an object is because I want to do polymorphic dispatch on the method name. myArray::shuffle() doesn't do that for me. No polymorphic dispatch. If myArray actually does have a shuffle method it isn't called. If that sort of direct function invocation is what I want, I'll just code a function call. No method invocation syntax and no |this| value is need.

Not so in the case of Russell's sketched "underscore2" (note the 2). Underscore (underscorejs.org) provides FP-style APIs hung off a _ object, e.g. _.map. Russell's myArray::shuffle() using a shuffle imported from underscore2 is clearly using an OO-style API from a made-up OO-style "underscore2".

I hope this is clear but fear it is not. I know it's not your main point -- that there's no "fall-forward" on Array.prototype.shuffle -- with which I agree. But it gets to why |this| matters.

I still don't get why so many JS programmer with a FP orientation want to do things with the |this| binding. |this| is for us OO geeks, if you are doing FP you don't need it. If you want to write shuffle, each, and filter functions just code them as normal functions passing the collection as the first argument.

That would be what underscore offers today, perhaps with better exports in an ES6 version (import {map, reduce} from "underscore-mod", no need for _.map, just call map as a function).

If I want to use you functions as a methods on one of my objects I'll just code something like: class { shuffle() {return shuffle(this)} }

Why would you both writing such boilerplate if you did not have to?

Assume that the problem of monkeypatching is solved somehow. Then two problems remain at play:

P1. How to select an extension in a method (not function) call, in a large-ish extent of code where one may want to select a non-extension too (on any type of object).

P2. How to "fall forward" when the extension is not needed because the built-in has the method.

Now, what might be useful would be :: that has approximately this semantics

import {shuffle,each,filter} from "underscore2"; myArray::shuffle();

desugars as

do {let _method = myArray[shuffle.name]; _method ? _method : shuffle}.call(myArray);

in other words, if myArray has a 'shuffle' method, call it; otherwise call the default shuffle method that I'm providing.

SOE (strawman:scoped_object_extensions) does this differently, by adding an extension object (associated with a prototype object) to a lexical scope, such that in that lexical scope, using an object that delegates to the extended prototype will use the extension, always. It will not "fall forward" to prefer a property from the object itself.

Quoting from strawman:scoped_object_extensions#property_lookup_spec_changes:

Section 8.12.1 [GetOwnProperty] is modified as follows:

When the [[GetOwnProperty]] internal method of O is called with property name P and lexical scope L, the following steps are taken:

  1. Let D be the result of calling [[GetExtensionProperty]] on object O with property name P and lexical scope L.
  2. If D is not undefined, return D.
  3. Else return [[GetUnextendedOwnProperty]] on object O with property name P.

When the [[GetExtensionProperty]] internal method of O is called with property name P and lexical scope L, the following steps are taken:

  1. If O doesn’t have an object extension in lexical scope L return undefined.
  2. Else let E be the object extension for O in lexical scope L.
  3. Return [[GetUnextendedOwnProperty]] with object E and property name P.

Ok, so maybe this lack of a (P2) solution is just an SOE design choice with which you disagree, but we need to be clear. Do we want fallback, or fall-forward, or neither? SOE was intended to provide an alternative to monkey-patching, with fallback when the named property is accessed on an unextended object. This is not the same as fall-forward, AKA "object detection".

Here I smell more "DWIM". People do not mean the same thing when they write (just the expression) myArray.shuffle(). The meaning depends, as you say, at least on polymorphic dispatch via the prototype chain in JS today. We could add more potential kinds of meaning, but TC39ers rejected adding a semi-static or dynamic scoped lookup on right of dot, per SOE.

What's left in my view is a combination of (a) monkey-patching as we know it (object detection is orthogonal and doable), or (b) some new operator that enables explicit "here is what I mean; do it" gesturing, or (c) the hypothetical, perhaps infeasible, static-only resolution system Andreas posed as a revived SOE requirement.

It so happens that the bind operator -- a solution of the (b) kind from last paragraph -- already solves problem (P1) above. That it does not solve (P2) is not a flaw in the bind operator. Again, SOE does not solve (P2)!

# Brendan Eich (11 years ago)

Russell Leggett wrote:

    The big issue I see here is chaining.
     `_.reduce(_.map(_.filter([1,2,3,4,5],x=>x%2 ===
    0),x=>2*x),(x,y)=>x+y)`
    Is a lot less readable than              
     `[1,2,3,4,5].filter(x=>x%2===0).map(x=>2*x).reduce((x,y)=>x+y))` 

P.S. This really doesn't look too shabby to me:

import {reduce,map,filter} from "underscore2"; [1,2,3,4,5]::filter(x=>x%2===0)::map(x=>2*x)::reduce((x,y)=>x+y))

Right, I think :: beats _. any day :-P.

Seriously, using underscore.js's _. is kind of sandbagging. Allen's right, FP-style looks fine but composes inside out rather than via chaining (FP'ers like this). No need for this or dot, if you can make the world (including built-ins) have this style of API.

Using your proposed "underscore2" (OO-underscore?) with :: is no more verbose than underscore.js (underscore1), and it has the chaining not inside-out-composing win some may prefer.

We should not argue only about taste, and bind (::) has a champion and good rationale as an addition to the language.

What I think we should argue about is whether SOE that solves either or both problems (P1 and P2) I identified is even possible. Andreas's static-only resolution requirement is good. Anyone want to work on that angle?

# Russell Leggett (11 years ago)

Using your proposed "underscore2" (OO-underscore?) with :: is no more verbose than underscore.js (underscore1), and it has the chaining not inside-out-composing win some may prefer.

I'm glad you noticed the 2. Perhaps oonderscore? :)

We should not argue only about taste, and bind (::) has a champion and good rationale as an addition to the language.

What I think we should argue about is whether SOE that solves either or both problems (P1 and P2) I identified is even possible. Andreas's static-only resolution requirement is good. Anyone want to work on that angle?

If we do assume static-only resolution, isn't that trivial to show its impossible? Wouldn't it require complete type inference? And even then, what about expressions that evaluate to more than one type?

Btw, another thing I noticed, thinking about using the binding operator this way, is that it's also a perfect fit for array-likes and array methods.

import {hide} from "jQuoory";
let {filter, forEach} = Array.prototype;
document.getElementsByClassName('hello')
    ::filter(e => e.tagName === "span")
    ::forEach(e => e::hide());
# Benjamin (Inglor) Gruenbaum (11 years ago)

Wait, I think maybe I did not understand what you meant before.

Are we talking about using :: for infixing the first parameter of the function? As in func(a,b,c) being the same as a::func(b,c) ?

Would that let us do `[1,2,3,4,5]::_.reduce(x=>x%2 ===

0)::.map(x=>2*x)::..reduce(x,y) => x+y))`? That could actually be pretty

nice, and if that lets us do extension methods that could also be pretty interesting.

Is this what you meant by :: and then having extension methods as an extra syntax?

If not, can you elaborate on ::? If yes, I'm all ears. This sounds like an interesting syntactic sugar that could help. It doesn't solve the problem of putting the method where it belongs but it does solve chaining quite nicely.

# Russell Leggett (11 years ago)

On Tue, Oct 15, 2013 at 4:28 PM, Benjamin (Inglor) Gruenbaum < inglor at gmail.com> wrote:

Wait, I think maybe I did not understand what you meant before.

Are we talking about using :: for infixing the first parameter of the function? As in func(a,b,c) being the same as a::func(b,c) ?

Not exactly. See the proposal strawman:bind_operator "Essentially, e1::e2 is equivalent to do { let obj=e1; e2.bind(obj) }"

Or, in the case of an immediate call, it skips the bind and just does a Function.prototype.call. In other words,

obj::fun(a,b) is not the same as fun(obj,a,b). Its fun.call(obj,a,b).

The important distinction is that the function being called is expecting a |this| as opposed to an extra first argument. This is reason I called it "underscore2" - Brendan picked up on that calling it OO underscore. Functional purists can argue with the approach, but it would let you write functions exactly as though they were extension methods - using |this| where appropriate.

The following is a somewhat silly example based on the SOE proposal

module Collections {
  export {
    where: Array.prototype.filter,
    select: Array.prototype.map
  }
}

module LolCatzDotCom {
  // imports Array.prototype extensions where and select into this

module import {where,select} from Collections;

  var allCatz = someArrayValue;
  // Array extensions are in scope
  var cuteCatNames = allCatz::where(cat => cat.isCute)::select(cat =>

cat.name); }

If you still don't get it, give me an example, and I can probably show the :: equivalent. What is your ideal SOE form? Because my guess is that I can get it darn close with ::

# Benjamin (Inglor) Gruenbaum (11 years ago)

I think I misunderstood :: before. if a::b(x_1,...,x_n) just means b(a,x_1,...,x_n) I think it might be a good solution to the chaining problem.

I think the .constructor proposal as well as being able to do :: completely eliminates the need for extension methods in this regard. It also behaves similarly to extension methods in C# in that it's just a static method and it could also introduce interesting options.

I'm not convinced about :: yet but it seemed to get a lot more positive feedback from community members I really value and bloggers I talked to since. The (small) research I'm doing about scoped extension methods or the problem does not seem to get nearly as good of a response.

The only issue here is:

Which polymorphism to people want? There is a "DWIM" aspect that cannot

possibly cover all uses of, e.g., 'draw' on the right of dot.

Let's say I have a classic prototypical inheritance use case. Cat.prototype.meow = function(){... Kitten.prototype = new Cat() Kitten.prototype.purr = function(){ ....

Now I have a catOrKitten object. If I define a function purr(catOrKitten) and call carOrKitten::purr() , regardless of it being a cat or a kitten - that function gets called. If I had an extension method on Cat.prototype, I'd get the ""correct"" behavior for kitten who overrides that method on its prototype.

# Benjamin (Inglor) Gruenbaum (11 years ago)

Thanks, this clarifies things (the difference between having this and a first argument is really not that big here at all imo and converting between them two is easy).

What is your ideal SOE form? Because my guess is that I can get it darn

close with ::

It does seem a lot simpler than actual extension methods with scope resolution, my only problem with it is the polymorphic case (the equally silly Cat/Kitten case in my email to Brendan). I'll ask around and have better conclusions but it does seem to have better feedback from developers, I think use cases like chaining are good for the wiki page.

# Brendan Eich (11 years ago)

Benjamin (Inglor) Gruenbaum <mailto:inglor at gmail.com> October 15, 2013 2:00 PM I think I misunderstood :: before. if a::b(x_1,...,x_n) just means b(a,x_1,...,x_n)

No, rather: b.call(a, x_1, ..., x_n) but with the original Function.prototype.call (not any shadowing b.call).

I think it might be a good solution to the chaining problem.

I think the .constructor proposal as well as being able to do :: completely eliminates the need for extension methods in this regard. It also behaves similarly to extension methods in C# in that it's just a static method and it could also introduce interesting options.

Indeed static methods with |this| uncurried are easier to call in the absence of ::, and this is why I added these so-called "static generics" to SpiderMonkey:

js> a = [1,2,3] [1, 2, 3] js> Array.map(a, x => xx) [1, 4, 9] js> Array.reduce(a, (r,x) => rx)

6 js> // etc.

Doing [].map.call(arraylike, mapfun) or worse, Array.prototype.map.call(arraylike, mapfun) is just no fun!

The only issue here is:

Which polymorphism to people want? There is a "DWIM" aspect that cannot possibly cover all uses of, e.g., 'draw' on the right of dot.

Let's say I have a classic prototypical inheritance use case. Cat.prototype.meow = function(){... Kitten.prototype = new Cat() Kitten.prototype.purr = function(){ ....

Now I have a catOrKitten object. If I define a function purr(catOrKitten) and call carOrKitten::purr()

This is based on your misunderstanding corrected above -- :: binds the object to the left of :: to |this|, not to the first argument.

regardless of it being a cat or a kitten - that function gets called. If I had an extension method on Cat.prototype, I'd get the ""correct"" behavior for kitten who overrides that method on its prototype.

I don't see purr on Cat.prototype --what am I missing?

Anyway, as Russell proposed, :: with imported methods (not |this|-free functions) is does call the named function, so without multimethods or any kind of dispatch based on arguments not receiver (this), you're right. You bind a method value to a name and call it on a given |this|.

Allen objected that this doesn't do receiver-based dispatch, which I think was your point with the cat and kitten. That's true, and SOE has that advantage -- kind of. SOE as I understand the strawman looks in the extension object first, and the extension object has no protototype object. It's flat. If you use an extension name on some other object that is not extended, of course you get polymorphic receiver-based dispatch.

# Andrea Giammarchi (11 years ago)

wait ... what ?

On Tue, Oct 15, 2013 at 2:00 PM, Russell Leggett <russell.leggett at gmail.com>wrote:

obj::fun(a,b) is not the same as fun(obj,a,b). Its fun.call(obj,a,b).

isn't this basically the equivalent of obj->fun then ? (yes, the other

arrow that was an arrow too far)

Thanks for clarification

# Andrea Giammarchi (11 years ago)

uhm, never mind, I got it now. Borrowing functions avoiding call/apply looks good.

Best

# Benjamin (Inglor) Gruenbaum (11 years ago)

On Wed, Oct 16, 2013 at 1:04 AM, Brendan Eich <brendan at mozilla.com> wrote:

No, rather: b.call(a, x_1, ..., x_n) but with the original

Function.prototype.call (not any shadowing b.call).

Right, Russell clarified :: to me and sent a link to the wiki (always good!). Thanks.

I don't see purr on Cat.prototype --what am I missing?

You're missing nothing. Cat doesn't have a purr on the prototype but Kitten whose prototype is a cat does.

By adding a scoped extension to Cat.protoype to the current module and then calling myObj.purr I'd expect:

  • If myObj is a Cat, the scoped extension will be called
  • If myObj is also a Kitten, Kitten.prototype.purr will be called instead since Kitten.prototype is sooner in the prototype chain.

This enables a sort of polymorphism in my opininion - whether it's very useful or not is another question but it's something the :: bind syntax doesn't really solve.

Allen objected that this doesn't do receiver-based dispatch, which I

think was your point with the cat and kitten. That's true,

Yes, that's the issue - but I'm no longer as convinced that the use case is strong enough to justify a whole new language construct.

# Andreas Rossberg (11 years ago)

On 15 October 2013 17:51, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

On Tue, Oct 15, 2013 at 3:39 AM, Andreas Rossberg <rossberg at google.com> wrote:

On 15 October 2013 03:09, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

I still don't get why so many JS programmer with a FP orientation want to do things with the |this| binding. |this| is for us OO geeks, if you are doing FP you don't need it.

Well, 'this' comes up because you cannot avoid having to interface with OOish APIs that conflicts with FPish composition patterns. For example, you'd like to be able to do something like

array.map(String.prototype.toLowerCase)

I'd be quite comfortable with saying: array.map(str=>str.toLowerCase())

It preserves the integrity of of the string abstraction rather than just assuming that the naked String.prototype.toLowerCase function is the appropriate one for any particular str value.

Yes, lambdas make it better. However, some more higher-order FP patterns are significantly more pleasant in point-free style. Not sure they matter much for common JS practice, though.

Using conventions:safe_meta_programming

var bind = Function.prototype.bind;
var uncurryThis = bind.bind(bind.call);

or, in a slightly less obscure formulation that ignores "safety"):

let uncurryThis = f=>((a0, ...args)=>f.apply(a0, args))

array.map(uncurryThis("".toLowerCase))

although, I still content that

    array.map(str=>str.toLowerCase())

is the better formulation. (and fewer characters for those who worry about such things)

Agreed. Any call that contains more than a single occurrence of apply/call/bind is just obfuscated code (and likely rather inefficient as well).

We need to know and respect abstraction boundaries. I think careful thought is called for each time you cross the FP/OO boundary. You can't just assume that the a function you extracted from a method property is the right function to use with other objects. You can't just assume that the |this| value of a method corresponds to the first parameter of a function (or visa vera).

Just because we know how an object is build out of functions, you shouldn't routinely take an object abstraction I've created and rip it apart into ins more primitive constituent elements which you then repurpose. To me, this is analogous to talking a function apart into its machine level code sequences and data structures and then doing clever things with them. It's possible, it might even be necessary in some very unusual situation, but it should never be routine.

I agree with most you say, and it's unfortunate that JS allows unsafe method extraction / this-stealing in the first place. But I wouldn't put the specific cases I'm alluding to into that category. They are mostly about bridging syntax asymmetries.

# Benjamin (Inglor) Gruenbaum (11 years ago)

Hey everyone.

I've talked to about 100 developers whose primary language is JS this last week (although I admit it was not a balanced survey, mainly people I know). Most of them (Over 80) do not feel that the problem I've mentioned before in scoped method extensions is a real problem they have to solve.

The main argument was that polyfilling (and as Brendan put it "prollyfilling") was good enough when not writing a library.

I am now convinced that he syntax overhead makes it not worth it in JavaScript. I don't know how "talking to developers" works here or how it is weighted, but I think it gave me a pretty good indication.

Thanks a lot for this discussion, I've learned a lot. Sorry if I wasted someone's time.

Benjamin Gruenbaum.