Arrow binding

# Alex Russell (13 years ago)

Despite making repeated arguments for "soft binding", I'm pretty sure I haven't outlined here what it actually would be. Now that we're looking to add syntactic forms that create bound function objects (arrows and class methods), perhaps it's time to get consensus for or against. Soft binding has 2 properties that make it desirable:

  • The global contract that methods can have their "this" re-set with .call() and .apply() is maintained

  • Common-case usage avoids the hazards of unbound and mis-appropriated "this" contexts. Most commonly, passing a method to a function which takes a callback:

    node.addEventListener("click", foo.bar);

The language today has 2 types of functions:

  • unbound: methods for which "this" is not fixed
  • hard-bound: methods bound by Function.prototype.bind()

Crucially, we have no syntax which creates hard-bound methods which means that they're not common (yet). To the extent that they are used, it is explicitly through forms like:

 node.addEventListener("click", foo.bar.bind(foo));

Or through libraries:

 dojo.connect(node, "onclick", foo, "bar");

This means that most users of most functions can still use .call() and .apply() without apprehension. Functions are still "just functions".

The new forms we're adding (methods and arrows) have the potential to change this radically, causing a large percentage of functions encountered by programmers to have binding. If that binding is hard-binding, .call() and .apply() break in the minds of users. Perhaps that's fine by you, but in addition to being a contractual failure, it removes a form of genericness which is unique in the language.

What to do?

One option is to barrel onward with either unbound functions, hard bound functions, or some mix thereof. These are all painful in ways I don't need to spend time here explaining. I propose a third alternative: soft binding (aka "preferred binding"). It enables the following:

 node.addEventListener("click", foo.bar.prefer(foo));

While still allowing the following:

 foo.bar.call(otherThis, …args);

Functions with preferred bindings can still be re-bound either with new preferred binding or with new hard binding (both forms vend new functions objects and they do today).

Here's a JSFiddle with an a quick ES5 desugaring + example:

  http://jsfiddle.net/slightlyoff/739CS/20/

Note that we need to re-define .call() and .apply() to be savvy to preferences, but this doesn't seem particularly painful. I've bluntly worked around it in this example to avoid proto re-wiring.

Thoughts?

-- Alex Russell slightlyoff at google.com slightlyoff at chromium.org alex at dojotoolkit.org BE03 E88D EABB 2116 CC49 8259 CF78 E242 59C3 9723

# Kevin Smith (13 years ago)

I'm not sure I understand the reasoning behind "soft-binding". If I use arrow syntax, my intention is to close over |this|. Allowing a caller to change the binding of |this| will result in a violation of that closure. In this respect, |this| within an arrow function is no different that any other closed-over variable. I would not want a caller to be able to override the binding on any of those variables, right?

# Russell Leggett (13 years ago)

On Mon, Apr 23, 2012 at 6:53 AM, Alex Russell <alex at dojotoolkit.org> wrote:

Despite making repeated arguments for "soft binding", I'm pretty sure I haven't outlined here what it actually would be. Now that we're looking to add syntactic forms that create bound function objects (arrows and class methods), perhaps it's time to get consensus for or against. Soft binding has 2 properties that make it desirable:

  • The global contract that methods can have their "this" re-set with .call() and .apply() is maintained

  • Common-case usage avoids the hazards of unbound and mis-appropriated "this" contexts. Most commonly, passing a method to a function which takes a callback:

    node.addEventListener("click", foo.bar);

The language today has 2 types of functions:

  • unbound: methods for which "this" is not fixed
  • hard-bound: methods bound by Function.prototype.bind()

Crucially, we have no syntax which creates hard-bound methods which means that they're not common (yet). To the extent that they are used, it is explicitly through forms like:

node.addEventListener("click", foo.bar.bind(foo));

Or through libraries:

dojo.connect(node, "onclick", foo, "bar");

This means that most users of most functions can still use .call() and .apply() without apprehension. Functions are still "just functions".

That is only true for functions that actually use |this|. Even though bind is probably not used in force yet because of cross-browser worries, "var self = this" is used everywhere. Functions using that pattern are no more usable with call/apply than arrow functions.

The new forms we're adding (methods and arrows) have the potential to change this radically, causing a large percentage of functions encountered by programmers to have binding. If that binding is hard-binding, .call() and .apply() break in the minds of users. Perhaps that's fine by you, but in addition to being a contractual failure, it removes a form of genericness which is unique in the language.

Last I checked, the new method form is still a dynamic binding - otherwise it wouldn't work. So really, you're just talking about arrow functions. In the cases where arrow functions make the most sense (callbacks) you rarely want a dynamic |this| - the oddball case being event handlers primarily.

What to do?

One option is to barrel onward with either unbound functions, hard bound functions, or some mix thereof. These are all painful in ways I don't need to spend time here explaining. I propose a third alternative: soft binding (aka "preferred binding"). It enables the following:

node.addEventListener("click", foo.bar.prefer(foo));

While still allowing the following:

foo.bar.call(otherThis, …args);

Functions with preferred bindings can still be re-bound either with new preferred binding or with new hard binding (both forms vend new functions objects and they do today).

Here's a JSFiddle with an a quick ES5 desugaring + example:

 http://jsfiddle.net/slightlyoff/739CS/20/

Note that we need to re-define .call() and .apply() to be savvy to preferences, but this doesn't seem particularly painful. I've bluntly worked around it in this example to avoid proto re-wiring.

Thoughts?

When I enumerate the use cases, I have trouble finding a good reason for soft-binding. You are correct that it removes a certain amount of genericness to use hard-binding, but I think writing a generic function should be intentional.

function foo(bar){
    bar.call(otherThis);
}

If you write a function foo that expects a function parameter bar, and you intend to override it's this value, that is a contract, and the |this| you substitute also needs to abide by the contract for use inside of the bar function. Traditionally, this would be done using a parameter to bar, instead of changing |this| which seems fragile at best. The only use case I see for changing |this| is if the function bar passed in is already an existing method.

//For this use case, why would I ever use an arrow function
foo( ()=>this.myBar() );

//instead of just passing the method directly
foo( this.myBar );

I really just don't see the value of changing the |this| value of a function created for the purpose of being an argument to a function. And frankly, I just don't see many other use cases for arrows. Maybe thats the part I'm missing.

# Alex Russell (13 years ago)

On Mon, Apr 23, 2012 at 2:47 PM, Russell Leggett <russell.leggett at gmail.com> wrote:

On Mon, Apr 23, 2012 at 6:53 AM, Alex Russell <alex at dojotoolkit.org> wrote:

Despite making repeated arguments for "soft binding", I'm pretty sure I haven't outlined here what it actually would be. Now that we're looking to add syntactic forms that create bound function objects (arrows and class methods), perhaps it's time to get consensus for or against. Soft binding has 2 properties that make it desirable:

* The global contract that methods can have their "this" re-set with .call() and .apply() is maintained  * Common-case usage avoids the hazards of unbound and mis-appropriated "this" contexts. Most commonly, passing a method to a function which takes a callback:

node.addEventListener("click", foo.bar);

The language today has 2 types of functions:

* unbound: methods for which "this" is not fixed   * hard-bound: methods bound by Function.prototype.bind()

Crucially, we have no syntax which creates hard-bound methods which means that they're not common (yet). To the extent that they are used, it is explicitly through forms like:

node.addEventListener("click", foo.bar.bind(foo));

Or through libraries:

dojo.connect(node, "onclick", foo, "bar");

This means that most users of most functions can still use .call() and .apply() without apprehension. Functions are still "just functions".

That is only true for functions that actually use |this|. Even though bind is probably not used in force yet because of cross-browser worries, "var self = this" is used everywhere. Functions using that pattern are no more usable with call/apply than arrow functions.

"everywhere" is incredibly strong wording, and I think that in the large, you're probably not correct. Some large % of code might manually "call their scope" through closure binding, but even that isn't an argument against soft binding.

The new forms we're adding (methods and arrows) have the potential to change this radically, causing a large percentage of functions encountered by programmers to have binding. If that binding is hard-binding, .call() and .apply() break in the minds of users. Perhaps that's fine by you, but in addition to being a contractual failure, it removes a form of genericness which is unique in the language.

Last I checked, the new method form is still a dynamic binding - otherwise it wouldn't work. So really, you're just talking about arrow functions. In the cases where arrow functions make the most sense (callbacks) you rarely want a dynamic |this| - the oddball case being event handlers primarily.

Having done this dance a couple of times, let me suggest to you that the method form will eventually end up at a per-class getter on the prototype which vends an instance function which is bound. People will (reasonably) want binding of some sort.

What to do?

One option is to barrel onward with either unbound functions, hard bound functions, or some mix thereof. These are all painful in ways I don't need to spend time here explaining. I propose a third alternative: soft binding (aka "preferred binding"). It enables the following:

node.addEventListener("click", foo.bar.prefer(foo));

While still allowing the following:

foo.bar.call(otherThis, …args);

Functions with preferred bindings can still be re-bound either with new preferred binding or with new hard binding (both forms vend new functions objects and they do today).

Here's a JSFiddle with an a quick ES5 desugaring + example:

jsfiddle.net/slightlyoff/739CS/20

Note that we need to re-define .call() and .apply() to be savvy to preferences, but this doesn't seem particularly painful. I've bluntly worked around it in this example to avoid proto re-wiring.

Thoughts?

When I enumerate the use cases, I have trouble finding a good reason for soft-binding. You are correct that it removes a certain amount of genericness to use hard-binding, but I think writing a generic function should be intentional.

function foo(bar){         bar.call(otherThis);     }

If you write a function foo that expects a function parameter bar, and you intend to override it's this value, that is a contract, and the |this| you substitute also needs to abide by the contract for use inside of the bar function. Traditionally, this would be done using a parameter to bar, instead of changing |this| which seems fragile at best. The only use case I see for changing |this| is if the function bar passed in is already an existing method.

//For this use case, why would I ever use an arrow function     foo( ()=>this.myBar() );

//instead of just passing the method directly     foo( this.myBar );

I really just don't see the value of changing the |this| value of a function created for the purpose of being an argument to a function. And frankly, I just don't see many other use cases for arrows. Maybe thats the part I'm missing.

Yeah, I think you're missing the composition arguments. If I create mixins with methods, they're going to have a promiscuious "this" as a feature. You might not write code like this today, but you probably should ;-)

# Kevin Smith (13 years ago)

Yeah, I think you're missing the composition arguments. If I create mixins with methods, they're going to have a promiscuious "this" as a feature. You might not write code like this today, but you probably should ;-)

But anyone who understands the language would not use arrow functions for that use case. I assume you're referring to something like this (Angus posted a similar example a while back):

function mixin() {
    this.{
        addMethodA() {},
        addMethodB() {}
    };
}

Your idea of a "preferred" binding for methods is interesting (as a form of method extraction), but I don't think it can be applied to arrow functions.

# Russell Leggett (13 years ago)

That is only true for functions that actually use |this|. Even though bind is probably not used in force yet because of cross-browser worries, "var self = this" is used everywhere. Functions using that pattern are no more usable with call/apply than arrow functions.

"everywhere" is incredibly strong wording, and I think that in the large, you're probably not correct. Some large % of code might manually "call their scope" through closure binding, but even that isn't an argument against soft binding.

Let me reword from "everywhere" to "extremely common". My point was that even though it is not used in every case, it is used often enough that |this| cannot be relied on. And my other point was that you have to know how the function passed in uses this, which makes it pretty tightly coupled.

Last I checked, the new method form is still a dynamic binding - otherwise it wouldn't work. So really, you're just talking about arrow functions. In the cases where arrow functions make the most sense (callbacks) you rarely want a dynamic |this| - the oddball case being event handlers primarily.

Having done this dance a couple of times, let me suggest to you that the method form will eventually end up at a per-class getter on the prototype which vends an instance function which is bound. People will (reasonably) want binding of some sort.

If that happens, then I will be more inclined to agree with you. Until that happens, I guess I just don't. Your argument seems balanced on something which you think will happen. I find that hard to agree with.

I really just don't see the value of changing the |this| value of a function created for the purpose of being an argument to a function. And frankly, I just don't see many other use cases for arrows. Maybe thats the part I'm missing.

Yeah, I think you're missing the composition arguments. If I create mixins with methods, they're going to have a promiscuious "this" as a feature. You might not write code like this today, but you probably should ;-)

I use mixins all the time, I just don't see them being being declared with arrow functions, which was the point I was trying to make here. If you are creating a mixin, then you would most likely want to use the new method syntax or at least the function syntax, but I think it would be silly to use the arrow function syntax, so I guess I'm just not sympathetic to that.

# Mark S. Miller (13 years ago)

On Mon, Apr 23, 2012 at 6:46 AM, Kevin Smith <khs4473 at gmail.com> wrote:

I'm not sure I understand the reasoning behind "soft-binding". If I use arrow syntax, my intention is to close over |this|. Allowing a caller to change the binding of |this| will result in a violation of that closure. In this respect, |this| within an arrow function is no different that any other closed-over variable. I would not want a caller to be able to override the binding on any of those variables, right?

Right. I think this is the key issue regarding defaults and what arrow functions must do -- hard binding. Making this binding soft would destroy the integrity of lexical capture that arrow functions provide for "this".

Alex, strawman:soft_bind, which we

worked on together, implements soft binding as a simple small library. We wrote this over a year ago. Since then, I've never found a need for this. Could you give a concrete example where this is useful? Do such uses justify a role beyond such library implementations? I'm inclined to YAGNI on this one.

# Axel Rauschmayer (13 years ago)

On Apr 23, 2012, at 16:17 , Alex Russell wrote:

Having done this dance a couple of times, let me suggest to you that the method form will eventually end up at a per-class getter on the prototype which vends an instance function which is bound. People will (reasonably) want binding of some sort.

An important use case. However, wouldn’t it be simpler to achieve the same goal by allowing the following two operations to co-exist?

  • "get": obj.method invokes the getter – which hard-binds on demand (but might cache via a weakmap).
  • "call": obj.method() invokes the actual method.

Hence, in addition to getters and setters, we would have "callers". If a property doesn’t have a caller then obj.method() would first invoke the getter and then try to call the result.

# John J Barton (13 years ago)

On Mon, Apr 23, 2012 at 3:53 AM, Alex Russell <alex at dojotoolkit.org> wrote:

The new forms we're adding (methods and arrows) have the potential to change this radically, causing a large percentage of functions encountered by programmers to have binding. If that binding is hard-binding, .call() and .apply() break in the minds of users. Perhaps that's fine by you, but in addition to being a contractual failure, it removes a form of genericness which is unique in the language.

Sorry if I missed reading about this but: why can't we re-bind 'this" in bound functions when using call(), apply() ? I'm sure it will make more work for JIT optimizers but they are very good and love this kind of problem.

jjb

# Alex Russell (13 years ago)

The semantic of Function.prototype.bind() precludes it. If we could relax that to be effectively soft-bound, I'm completely on board, but I expect it will cause some problems.

We could, of course, try this out in Chrome and see how it goes.

# Oliver Hunt (13 years ago)

On Apr 23, 2012, at 8:32 AM, John J Barton wrote:

On Mon, Apr 23, 2012 at 3:53 AM, Alex Russell <alex at dojotoolkit.org> wrote:

The new forms we're adding (methods and arrows) have the potential to change this radically, causing a large percentage of functions encountered by programmers to have binding. If that binding is hard-binding, .call() and .apply() break in the minds of users. Perhaps that's fine by you, but in addition to being a contractual failure, it removes a form of genericness which is unique in the language.

Sorry if I missed reading about this but: why can't we re-bind 'this" in bound functions when using call(), apply() ? I'm sure it will make more work for JIT optimizers but they are very good and love this kind of problem.

Because the semantics would be difficult to implement -- jit or otherwise -- and difficult to specify otherwise :-(

The more serious issue though is that of program behaviour -- if a developer has strongly bound this, it is reasonable to expect that they had some reason to do so. If you provide a mechanism that can bypass that binding you can break program abstraction, potentially allowing security bugs in the SES/Caja type language models.

It would also imply being able to rebind |this| in bound functions, which would be insane.

# Alex Russell (13 years ago)

On Apr 23, 2012, at 3:30 PM, Russell Leggett wrote:

That is only true for functions that actually use |this|. Even though bind is probably not used in force yet because of cross-browser worries, "var self = this" is used everywhere. Functions using that pattern are no more usable with call/apply than arrow functions.

"everywhere" is incredibly strong wording, and I think that in the large, you're probably not correct. Some large % of code might manually "call their scope" through closure binding, but even that isn't an argument against soft binding.

Let me reword from "everywhere" to "extremely common". My point was that even though it is not used in every case, it is used often enough that |this| cannot be relied on. And my other point was that you have to know how the function passed in uses this, which makes it pretty tightly coupled.

Last I checked, the new method form is still a dynamic binding - otherwise it wouldn't work. So really, you're just talking about arrow functions. In the cases where arrow functions make the most sense (callbacks) you rarely want a dynamic |this| - the oddball case being event handlers primarily.

Having done this dance a couple of times, let me suggest to you that the method form will eventually end up at a per-class getter on the prototype which vends an instance function which is bound. People will (reasonably) want binding of some sort.

If that happens, then I will be more inclined to agree with you. Until that happens, I guess I just don't. Your argument seems balanced on something which you think will happen. I find that hard to agree with.

I really just don't see the value of changing the |this| value of a function created for the purpose of being an argument to a function. And frankly, I just don't see many other use cases for arrows. Maybe thats the part I'm missing.

Yeah, I think you're missing the composition arguments. If I create mixins with methods, they're going to have a promiscuious "this" as a feature. You might not write code like this today, but you probably should ;-)

I use mixins all the time, I just don't see them being being declared with arrow functions, which was the point I was trying to make here. If you are creating a mixin, then you would most likely want to use the new method syntax or at least the function syntax, but I think it would be silly to use the arrow function syntax, so I guess I'm just not sympathetic to that.

If methods on classes sprout binding of some sort, this will be the natural style. And I think that's a good place to be. If both arrow and class methods lexically soft-bind, truly unbound functions will be declared as "function" and will be easy to spot.

# Alex Russell (13 years ago)

On Apr 23, 2012, at 3:35 PM, Mark S. Miller wrote:

On Mon, Apr 23, 2012 at 6:46 AM, Kevin Smith <khs4473 at gmail.com> wrote: I'm not sure I understand the reasoning behind "soft-binding". If I use arrow syntax, my intention is to close over |this|.

Arrow will take on whatever meaning we decide for it to take on. You have a particular desugaring in mind, but making the call that "I'd like all of the effects of closing over |this|" vs. the value of "I'd like this not to blow-up when I de-ref it from my object" is not something that's easy to inspect for. Assuming preference for the hard version is just that, assumption.

Allowing a caller to change the binding of |this| will result in a violation of that closure. In this respect, |this| within an arrow function is no different that any other closed-over variable. I would not want a caller to be able to override the binding on any of those variables, right?

Right. I think this is the key issue regarding defaults and what arrow functions must do -- hard binding. Making this binding soft would destroy the integrity of lexical capture that arrow functions provide for "this".

Alex, strawman:soft_bind, which we worked on together, implements soft binding as a simple small library. We wrote this over a year ago.

Ah, yeah, I'd actually forgotten about it.

Since then, I've never found a need for this. Could you give a concrete example where this is useful? Do such uses justify a role beyond such library implementations? I'm inclined to YAGNI on this one.

Well, since it's much larger (code-wise) than the equivalent hard-binding method, it's not hard to see which one you'd include in a JS library. You'd need the soft version a lot to meet the overhead requirements of including both in a JS lib.

What syntax does, however, is a very different question and using library evidence by wrote gets you into trouble. Our view from the language perspective can be broader and can look for conceptual integrity where libraries cannot afford to.

# Brandon Benvie (13 years ago)

The real use case that I constantly run into has nothing to do with binding at all, rather the need for Function.prototype.partial and no specific binding. The primary use case for soft binding or dynamic non-method binding, as mentioned above, is the event callback and that's weird semantic captured in the way the DOM works. The problem is that with JS it's a prevalent use case because of how closely associated JS has been with the DOM.

# David Herman (13 years ago)

On Apr 23, 2012, at 3:53 AM, Alex Russell wrote:

This means that most users of most functions can still use .call() and .apply() without apprehension. Functions are still "just functions".

You're assuming your conclusion: that there's some sort of platonic ideal ("just functions") of JS functions always having a use for their this binding. But when I pass function() { elt.style.background = 'yellow' } to setTimeout, is my function some betrayal of The JavaScript Way? No it's a function that changes the background color of an element. It's not evil. It's every bit as valid a function as one that's intended to be a method.

JavaScript uses functions for many different things: constructors, top-level functions, callbacks, and methods. Sometimes you need all their features, sometimes you don't.

The new forms we're adding (methods and arrows) have the potential to change this radically, causing a large percentage of functions encountered by programmers to have binding. If that binding is hard-binding, .call() and .apply() break in the minds of users. Perhaps that's fine by you, but in addition to being a contractual failure, it removes a form of genericness which is unique in the language.

I don't accept this frame. The contract of .call() and .apply() is that their first argument specifies the binding for this. The contract between a function and its caller is that the caller provides bindings for the arguments, and the callee decides what to do with those arguments. It is perfectly acceptable for the callee to disregard some arguments.

Now, it's certainly possible to make functions less abstract than necessary by using closed-over variables instead of arguments (including this). Maybe where I'm closer to agreement with you is that when you're implementing methods it's better to use this than to lexically close over the object the methods were created for. But not all functions in JavaScript are methods.

I'm much more sympathetic to the idea of having two shorter-function syntaxes, one optimized for methods and one optimized for non-method functions. I understand the concern about bloat, but to me it addresses the reality of different contexts in programming, e.g.:

a.map(x => x + 1)

vs

box = {
    _value: 0,
    get: () -> this._value,
    set(v) { this._value = v }
}

One option is to barrel onward with either unbound functions, hard bound functions, or some mix thereof. These are all painful in ways I don't need to spend time here explaining.

Maybe you do need to, because I'm not convinced. JavaScript already lets you do either. With the shorter function syntax, we've only been discussing conveniences for things you can already express. You claim that conveniences like fat arrow are "radical," but soft bind is the proposal that breaks an existing language guarantee. It introduces a new distinction: a callee can now change behavior depending on whether it was called via f() or vs f.call(null). That breaks the feature that you can always use .call/.apply as a drop-in replacement for the equivalent explicit call.

# Tab Atkins Jr. (13 years ago)

On Mon, Apr 23, 2012 at 3:36 PM, David Herman <dherman at mozilla.com> wrote:

I'm much more sympathetic to the idea of having two shorter-function syntaxes, one optimized for methods and one optimized for non-method functions. I understand the concern about bloat, but to me it addresses the reality of different contexts in programming, e.g.:

a.map(x => x + 1)

vs

box = {        _value: 0,        get: () -> this._value,        set(v) { this._value = v }    }

I don't understand this example. What is "set(v) {...}"? It's not a setter, because it's not associated with a property name. Assuming you meant "set value(v) {...}", why not just use an ordinary getter as well?

# Rick Waldron (13 years ago)

On Tue, Apr 24, 2012 at 12:33 PM, Tab Atkins Jr. <jackalmage at gmail.com>wrote:

On Mon, Apr 23, 2012 at 3:36 PM, David Herman <dherman at mozilla.com> wrote:

I'm much more sympathetic to the idea of having two shorter-function syntaxes, one optimized for methods and one optimized for non-method functions. I understand the concern about bloat, but to me it addresses the reality of different contexts in programming, e.g.:

a.map(x => x + 1)

vs

box = { _value: 0, get: () -> this._value, set(v) { this._value = v } }

I don't understand this example. What is "set(v) {...}"? It's not a setter, because it's not associated with a property name. Assuming you meant "set value(v) {...}", why not just use an ordinary getter as well?

When I read this, I assumed it was a reference to this: harmony:object_literals#object_literal_property_shorthands

Which would produce a method named "set" on "box"

box.set(42) box.get()

42

The example is easily confused.

Hope that helps

# Axel Rauschmayer (13 years ago)

Seems like a typo and Tab’s got it right. The advantage of a thin arrow (over a method definition) here is that you can do an implicit return and have slightly less to type. Given that method definitions don’t appear inside statements, I don’t see that as a problem.

AFAIK, an implicit return of the completion value of a method is out, because one might involuntary return wrong values (previously: undefined, now: completion value).

# Tab Atkins Jr. (13 years ago)

On Tue, Apr 24, 2012 at 9:51 AM, Rick Waldron <waldron.rick at gmail.com> wrote:

When I read this, I assumed it was a reference to this: harmony:object_literals#object_literal_property_shorthands

Which would produce a method named "set" on "box"

box.set(42) box.get()

42

The example is easily confused.

Ah, that makes sense. Okay.

In that case, I don't see why one wouldn't just use the same syntax for both methods:

let box = { _value: 0, get() { return this._value; }, set(v) { this._value = v; } };

There doesn't seem to be a need there for thin-arrow (dynamic this) functions.

(Edit: Oh, I see, leaning on the completion value of thin-arrow functions here lets you shave a few characters off. The readability loss of mixing the two syntaxes doesn't seem worthwhile, though.)

In fact, we can enumerate the different situations functions can appear, since they're all gaining nice syntax:

  1. Method on a class - when we get max/min classes, these'll have a nice syntax, and will have dynamic-this behavior, just what we want.
  2. Method on an object literal - if we have the syntax extension outlined above, you also get dynamic-this, which we want.
  3. Method dynamically attached to an object - here we don't have special syntax, so we'll still need to use the "function" keyword.
  4. Non-method function - you don't want a dynamic this, you want to lexically close over all your variables. Fat-arrows do this for you. (The only reason to want dynamic this on a non-method is to do a poor-man's currying. Get a real curry function; this isn't worth the bugs.)

The only case that isn't receiving special syntax (and thus which could potentially benefit from a dynamic-this thin-arrow function) is .#3. However, while #3 is common today, #1 and #2 will eat most of its share. I'm not sure that the remaining #3 cases will be worth a special syntax.

If they are, though, nothing's closing the door on a thin-arrow in the future. No reason to block fat-arrow based on this.

# Rick Waldron (13 years ago)

On Tue, Apr 24, 2012 at 1:03 PM, Tab Atkins Jr. <jackalmage at gmail.com>wrote:

On Tue, Apr 24, 2012 at 9:51 AM, Rick Waldron <waldron.rick at gmail.com> wrote:

When I read this, I assumed it was a reference to this: harmony:object_literals#object_literal_property_shorthands

Which would produce a method named "set" on "box"

box.set(42) box.get()

42

The example is easily confused.

Ah, that makes sense. Okay.

In that case, I don't see why one wouldn't just use the same syntax for both methods:

let box = { _value: 0, get() { return this._value; }, set(v) { this._value = v; } };

There doesn't seem to be a need there for thin-arrow (dynamic this) functions.

I think the colon was just a typo

Rick

[snip]

# Kevin Smith (13 years ago)
  1. Method dynamically attached to an object - here we don't have special syntax, so we'll still need to use the "function" keyword.

Not necessarily. Object literal extensions are a possibility here, especially if you are attaching more than one method:

obj.{
    addMethodA() { ... },
    addMethodB() { ... }
};
# Brendan Eich (13 years ago)

Rick Waldron wrote:

I think the colon was just a typo

Dave is not cc'ed and may have missed the responses going back to Tab's first one, but I think there was no typo. Dave's example:

 box = {
     _value: 0,
     get: () ->  this._value,
     set(v) { this._value = v }
 }

Yes, it's awkward as examples go because get and set are contextual keywords, but this shows them used as plain identifiers. This also shows -> as property value in an object literal (for property name 'get'),

well as method definition shorthand (for 'set'), to contrast the two styles.

# Brendan Eich (13 years ago)

Tab Atkins Jr. wrote:

The only case that isn't receiving special syntax (and thus which could potentially benefit from a dynamic-this thin-arrow function) is #3. However, while #3 is commontoday, #1 and #2 will eat most of its share. I'm not sure that the remaining #3 cases will be worth a special syntax.

If they are, though, nothing's closing the door on a thin-arrow in the future. No reason to block fat-arrow based on this.

Nicely put and I agree -- but I'm open to "the future" being sooner than we think. Getting => past TC39 took some doing, don't want to break

consensus over pushing ->. Do want to get to -> if the use-cases from

the field are strong enough.

# Rick Waldron (13 years ago)

On Tue, Apr 24, 2012 at 4:21 PM, Brendan Eich <brendan at mozilla.org> wrote:

Rick Waldron wrote:

I think the colon was just a typo

Dave is not cc'ed and may have missed the responses going back to Tab's first one, but I think there was no typo. Dave's example:

box = { _value: 0, get: () -> this._value, set(v) { this._value = v } }

Yes, it's awkward as examples go because get and set are contextual keywords, but this shows them used as plain identifiers. This also shows -> as property value in an object literal (for property name 'get'), well as method definition shorthand (for 'set'), to contrast the two styles.

This makes complete sense now, thank you for the clarification.

# David Herman (13 years ago)

On Apr 24, 2012, at 9:51 AM, Rick Waldron wrote:

On Tue, Apr 24, 2012 at 12:33 PM, Tab Atkins Jr. <jackalmage at gmail.com> wrote: On Mon, Apr 23, 2012 at 3:36 PM, David Herman <dherman at mozilla.com> wrote:

box = { _value: 0, get: () -> this._value, set(v) { this._value = v } }

I don't understand this example. What is "set(v) {...}"? It's not a setter, because it's not associated with a property name. Assuming you meant "set value(v) {...}", why not just use an ordinary getter as well?

When I read this, I assumed it was a reference to this: harmony:object_literals#object_literal_property_shorthands

Right. Thanks for providing the reference.

Which would produce a method named "set" on "box"

box.set(42) box.get()

42

The example is easily confused.

Yeah, my bad. I was trying to demonstrate several syntaxes but shouldn't've picked an example with a method named "set". To clarify the example:

box = {
    _value: 0,
    unbox: () -> this._value,
    setbox(v) { this._value = v }
}
# David Herman (13 years ago)

On Apr 24, 2012, at 10:03 AM, Tab Atkins Jr. wrote:

There doesn't seem to be a need there for thin-arrow (dynamic this) functions.

(Edit: Oh, I see, leaning on the completion value of thin-arrow functions here lets you shave a few characters off. The readability loss of mixing the two syntaxes doesn't seem worthwhile, though.)

Lets you eliminate "{" and "}" and "return", but requires additional ":" and "->". Net savings of 3 characters. But more than that, eliminating "return" lets you write methods in a functional style without needing the explicit control flow operator.

The only case that isn't receiving special syntax (and thus which could potentially benefit from a dynamic-this thin-arrow function) is #3. However, while #3 is common today, #1 and #2 will eat most of its share. I'm not sure that the remaining #3 cases will be worth a special syntax.

Classes certainly make it less necessary, given that cases like:

C.prototype.m1 = function(...) { ... };
C.prototype.m2 = function(...) { ... };
C.prototype.m3 = function(...) { ... };

will often be replaced by classes. But I am less sure it'll go away.

If they are, though, nothing's closing the door on a thin-arrow in the future. No reason to block fat-arrow based on this.

I'm definitely not advocating blocking fat-arrow! I favor both of them, though IMO fat-arrow is more important.

# Allen Wirfs-Brock (13 years ago)

On Apr 24, 2012, at 1:40 PM, David Herman wrote:

On Apr 24, 2012, at 10:03 AM, Tab Atkins Jr. wrote:

There doesn't seem to be a need there for thin-arrow (dynamic this) functions.

(Edit: Oh, I see, leaning on the completion value of thin-arrow functions here lets you shave a few characters off. The readability loss of mixing the two syntaxes doesn't seem worthwhile, though.)

Lets you eliminate "{" and "}" and "return", but requires additional ":" and "->". Net savings of 3 characters. But more than that, eliminating "return" lets you write methods in a functional style without needing the explicit control flow operator.

I believe that we could grammatically use ArrowBody as the body of concise methods in object literals and classes. In that case, Dave's example could be expressed as:

box = {
    _value: 0,
    unbox() ()-> this._value,
    setbox(v)  this._value = v 
}

It could also be used as the body of getter/setters

val = {
    _value: 0,
    get value() this._value,
    set value(v)  this._value = v 
}
# Axel Rauschmayer (13 years ago)

I believe that we could grammatically use ArrowBody as the body of concise methods in object literals and classes. In that case, Dave's example could be expressed as:

box = {
    _value: 0,
    unbox() ()-> this._value,
    setbox(v)  this._value = v 
}

That would be very nice. And it would be a perfect solution for the completion value issue: If you want to implicitly return the completion value, use an expression body.

Is there a typo in the code? I’d expect it to be:

box = {
    _value: 0,
    unbox() this._value,
    setbox(v)  this._value = v
}
# Brendan Eich (13 years ago)

Allen Wirfs-Brock wrote:

    unbox() ()-> this._value,

Left-over ()-> in the middle, right?

# David Herman (13 years ago)

On Apr 24, 2012, at 2:09 PM, Allen Wirfs-Brock wrote:

On Apr 24, 2012, at 1:40 PM, David Herman wrote:

On Apr 24, 2012, at 10:03 AM, Tab Atkins Jr. wrote:

There doesn't seem to be a need there for thin-arrow (dynamic this) functions.

(Edit: Oh, I see, leaning on the completion value of thin-arrow functions here lets you shave a few characters off. The readability loss of mixing the two syntaxes doesn't seem worthwhile, though.)

Lets you eliminate "{" and "}" and "return", but requires additional ":" and "->". Net savings of 3 characters. But more than that, eliminating "return" lets you write methods in a functional style without needing the explicit control flow operator.

I believe that we could grammatically use ArrowBody as the body of concise methods in object literals and classes. In that case, Dave's example could be expressed as:

box = {
    _value: 0,
    unbox() ()-> this._value,

ITYM:

unbox() this._value,
    setbox(v)  this._value = v 
}

It could also be used as the body of getter/setters

val = {
    _value: 0,
    get value() this._value,
    set value(v)  this._value = v 
}

This all seems reasonable to me. I guess I see a couple reasonable alternatives here:

A. add thin arrow, add expression bodies to method shorthands

(-) Might be a bit much
(+) But all around a goodly set of convenience forms

B. add expression bodies to method shorthands, don't add skinny arrow

(-) Loses the flexibility of shorthand syntax for assigning to an existing object, a Tab pointed out. (Sorry Axel, mustache is not particularly Harmonious.)
(+) Keeps us to just one arrow form

C. eliminate method shorthands, add skinny arrow

(-) Loses the method shorthand in object literals
(-) Methods in classes would remain the same, which loses some symmetry between object literals and classes
(+) Eliminates visual confusion between getters/setters and method shorthand, but still with a minimal syntax (i.e., "->")

All three of these options look pretty decent to me. I think I'd be pretty happy with any of them.

# Allen Wirfs-Brock (13 years ago)

On Apr 24, 2012, at 2:44 PM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

   unbox() ()-> this._value,

Left-over ()-> in the middle, right?

yes, that's right. it should be unbox () this.value,

I was actually confusing that example with something

 thunk () () => this._value,
# Axel Rauschmayer (13 years ago)

B. add expression bodies to method shorthands, don't add skinny arrow

(-) Loses the flexibility of shorthand syntax for assigning to an existing object, a Tab pointed out. (Sorry Axel, mustache is not particularly Harmonious.)

Sorry to hear that. _.extend(), then (by whichever name)? As long as this operation can be performed in some manner...

(+) Keeps us to just one arrow form

C. eliminate method shorthands, add skinny arrow

(-) Loses the method shorthand in object literals
(-) Methods in classes would remain the same, which loses some symmetry between object literals and classes
(+) Eliminates visual confusion between getters/setters and method shorthand, but still with a minimal syntax (i.e., "->")

That would make super-references more difficult to implement, right? You’d have to invoke a defineMethod() somewhere along the way.

# Allen Wirfs-Brock (13 years ago)

On Apr 24, 2012, at 3:30 PM, Axel Rauschmayer wrote:

B. add expression bodies to method shorthands, don't add skinny arrow

(-) Loses the flexibility of shorthand syntax for assigning to an existing object, a Tab pointed out. (Sorry Axel, mustache is not particularly Harmonious.)

Sorry to hear that. _.extend(), then (by whichever name)? As long as this operation can be performed in some manner...

or we need to find mustached harmony, perhaps with some slight syntax change. The utility of .{ seems to be showing up fairly frequently in these discussions and also fits quite nicely with max-min classes for both instance and class property definition.

# Axel Rauschmayer (13 years ago)

Using a builtin function (as opposed to an operator) would be less problematic if one could invoke it infix, e.g.:

 this (extend) { x: x, y: y }

But I guess deciding on that will have to wait until operator overloading gets figured out.

# Allen Wirfs-Brock (13 years ago)

On Apr 24, 2012, at 3:53 PM, Axel Rauschmayer wrote:

Using a builtin function (as opposed to an operator) would be less problematic if one could invoke it infix, e.g.:

 this (extend) { x: x, y: y }

That wouldn't address the issues concerning cloning private names, super binding, etc. I believe Dave's main issue is with the exact .{ syntax. I'm flexible if that can be be harmoniously resolved with a minor syntactic change such as:

this.={methodA() {}, methodB() {} }; or

this.+{methodA() {}, methodB() {} };

or something else

this.&{methodA() {}, methodB() {} };

# Kevin Smith (13 years ago)

I wasn't convinced at first but I think the obj.{ ... } extension syntax provides good utility. I would strongly suggest not giving it up. I've used it in several refactoring examples on this list.

And I still don't see the attraction of "thin arrow". It provides no semantic benefit and only very arguable syntactic benefit. Object literal methods and arrows eliminate the pain of "function" syntax for pretty much every place where it is a pain. The ability to eliminate "return ..." from method bodies is a very dubious benefit; I believe that in the case of methods this will make code less readable.

There seems to be a general assumption that "arrows are the new function". This comes from CoffeeScript, I suppose, but in my opinion following that route will neither make the language more readable nor expressive.