arrows and a proposed softCall
On Tue, May 29, 2012 at 11:50 PM, Yehuda Katz <wycats at gmail.com> wrote:
I'm not sure if this makes sense, but something about breaking
call
andapply
doesn't sit right with me.
Why should an arrow function be treated any differently than an explicitly bound function (ie, via bind)?
All "soft-bind" proposals I have heard have failed to get traction for two reasons:
-
Binding |this| makes an abstraction with its own integrity. Overriding the |this| binding only some of the time violates that abstraction.
-
Implementations including beloved V8 and venerable SpiderMonkey would have to recompile target soft-bound functions, or else provide an extra parameter to replace what is for =>a lexical up-var reference. And extra
parameters and/or recompilation steps cost performance and code complexity, always.
Whatever you think of (1) -- hey, sometimes abstractions need to be broken (I'm not being sarcastic in general, but for => I disagree with
this aside) -- (2) is a big problem. If V8 and SpiderMonkey won't go for "soft-bind", there's no hope.
On Tue, May 29, 2012 at 11:52 PM, John Tamplin <jat at google.com> wrote:
On Tue, May 29, 2012 at 11:50 PM, Yehuda Katz <wycats at gmail.com> wrote:
I'm not sure if this makes sense, but something about breaking
call
andapply
doesn't sit right with me.Why should an arrow function be treated any differently than an explicitly bound function (ie, via bind)?
Hah, you beat me too it, I was typing in almost the exact same thing. And not just bind, either - even the kind of workarounds people would use before bind like 'var self = this' sorts of things. If someone writes code using the arrow, what they want is to use the lexically scoped this - they want to use 'this' just like any other reference captured by the closure.
Any use of call or apply has to be done carefully and requires knowledge of how 'this' is used. It effectively becomes another parameter to the function. In cases of bind or an arrow function, 'this' is removed as a parameter and is purely another captured reference. There's no special call function to let you override other closed over values. Why should arrow function's behavior for 'this' be any different?
On Wednesday, May 30, 2012 at 12:00 AM, Russell Leggett wrote:
On Tue, May 29, 2012 at 11:52 PM, John Tamplin <jat at google.com (mailto:jat at google.com)> wrote:
On Tue, May 29, 2012 at 11:50 PM, Yehuda Katz <wycats at gmail.com (mailto:wycats at gmail.com)> wrote:
I'm not sure if this makes sense, but something about breaking
call
andapply
doesn't sit right with me.Why should an arrow function be treated any differently than an explicitly bound function (ie, via bind)?
Hah, you beat me too it, I was typing in almost the exact same thing. And not just bind, either - even the kind of workarounds people would use before bind like 'var self = this' sorts of things. If someone writes code using the arrow, what they want is to use the lexically scoped this - they want to use 'this' just like any other reference captured by the closure.
Any use of call or apply has to be done carefully and requires knowledge of how 'this' is used. It effectively becomes another parameter to the function. In cases of bind or an arrow function, 'this' is removed as a parameter and is purely another captured reference. There's no special call function to let you override other closed over values. Why should arrow function's behavior for 'this' be any different?
There is also no reason to think of => as all or nothing, jQuery and it's APIs can still use function expressions where dynamic |this| is expected. If a user tries to pass => to any of these APIs, they will learn quickly that they've done it wrong.
On May 29, 2012, at 10:05 PM, Rick Waldron wrote:
On Wednesday, May 30, 2012 at 12:00 AM, Russell Leggett wrote:
On Tue, May 29, 2012 at 11:52 PM, John Tamplin <jat at google.com> wrote: On Tue, May 29, 2012 at 11:50 PM, Yehuda Katz <wycats at gmail.com> wrote: I'm not sure if this makes sense, but something about breaking
call
andapply
doesn't sit right with me.Why should an arrow function be treated any differently than an explicitly bound function (ie, via bind)?
Hah, you beat me too it, I was typing in almost the exact same thing. And not just bind, either - even the kind of workarounds people would use before bind like 'var self = this' sorts of things. If someone writes code using the arrow, what they want is to use the lexically scoped this - they want to use 'this' just like any other reference captured by the closure.
Any use of call or apply has to be done carefully and requires knowledge of how 'this' is used. It effectively becomes another parameter to the function. In cases of bind or an arrow function, 'this' is removed as a parameter and is purely another captured reference. There's no special call function to let you override other closed over values. Why should arrow function's behavior for 'this' be any different? There is also no reason to think of => as all or nothing, jQuery and it's APIs can still use function expressions where dynamic |this| is expected. If a user tries to pass => to any of these APIs, they will learn quickly that they've done it wrong.
However, there might be some utility (at least for debugging) in having a Function.prototype.isThisBound method. It would return true for arrow functions and functions created via Function.prototype.bind
On Wed, May 30, 2012 at 1:32 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:
On May 29, 2012, at 10:05 PM, Rick Waldron wrote:
On Wednesday, May 30, 2012 at 12:00 AM, Russell Leggett wrote:
On Tue, May 29, 2012 at 11:52 PM, John Tamplin <jat at google.com> wrote:
On Tue, May 29, 2012 at 11:50 PM, Yehuda Katz <wycats at gmail.com> wrote:
I'm not sure if this makes sense, but something about breaking
call
andapply
doesn't sit right with me.Why should an arrow function be treated any differently than an explicitly bound function (ie, via bind)?
Hah, you beat me too it, I was typing in almost the exact same thing. And not just bind, either - even the kind of workarounds people would use before bind like 'var self = this' sorts of things. If someone writes code using the arrow, what they want is to use the lexically scoped this - they want to use 'this' just like any other reference captured by the closure.
Any use of call or apply has to be done carefully and requires knowledge of how 'this' is used. It effectively becomes another parameter to the function. In cases of bind or an arrow function, 'this' is removed as a parameter and is purely another captured reference. There's no special call function to let you override other closed over values. Why should arrow function's behavior for 'this' be any different?
There is also no reason to think of => as all or nothing, jQuery and it's APIs can still use function expressions where dynamic |this| is expected. If a user tries to pass => to any of these APIs, they will learn quickly that they've done it wrong.
However, there might be some utility (at least for debugging) in having a Function.prototype.isThisBound method. It would return true for arrow functions and functions created via Function.prototype.bind
This was actually expressed by the JSFixed group - that a means of detecting bound functions would essentially mitigate any perceived pain points of fat-arrow-functions. If we can get consensus on something like this, it would allay community concerns about the fat arrow.
I would make one minor suggestion, and that is to shorten it to "isBound"
On Wed, May 30, 2012 at 10:32 AM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:
On May 29, 2012, at 10:05 PM, Rick Waldron wrote:
On Wednesday, May 30, 2012 at 12:00 AM, Russell Leggett wrote:
On Tue, May 29, 2012 at 11:52 PM, John Tamplin <jat at google.com> wrote:
On Tue, May 29, 2012 at 11:50 PM, Yehuda Katz <wycats at gmail.com> wrote:
I'm not sure if this makes sense, but something about breaking
call
andapply
doesn't sit right with me.Why should an arrow function be treated any differently than an explicitly bound function (ie, via bind)?
Hah, you beat me too it, I was typing in almost the exact same thing. And not just bind, either - even the kind of workarounds people would use before bind like 'var self = this' sorts of things. If someone writes code using the arrow, what they want is to use the lexically scoped this - they want to use 'this' just like any other reference captured by the closure.
Any use of call or apply has to be done carefully and requires knowledge of how 'this' is used. It effectively becomes another parameter to the function. In cases of bind or an arrow function, 'this' is removed as a parameter and is purely another captured reference. There's no special call function to let you override other closed over values. Why should arrow function's behavior for 'this' be any different?
There is also no reason to think of => as all or nothing, jQuery and it's APIs can still use function expressions where dynamic |this| is expected. If a user tries to pass => to any of these APIs, they will learn quickly that they've done it wrong.
However, there might be some utility (at least for debugging) in having a Function.prototype.isThisBound method. It would return true for arrow functions and functions created via Function.prototype.bind
I agree that such a predicate would be useful, but the useful predicate should also give "true" for functions that don't mention "this". Thus, "isThisBound" may not be the right name. What it really means is 'Can this function sense the "this" binding with which it is called?" If we call it "canSenseThis", then clearly we should also reverse the truth value.
On May 30, 2012, at 2:18 PM, Mark S. Miller wrote:
On Wed, May 30, 2012 at 10:32 AM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
...
However, there might be some utility (at least for debugging) in having a Function.prototype.isThisBound method. It would return true for arrow functions and functions created via Function.prototype.bind
I agree that such a predicate would be useful, but the useful predicate should also give "true" for functions that don't mention "this". Thus, "isThisBound" may not be the right name. What it really means is 'Can this function sense the "this" binding with which it is called?" If we call it "canSenseThis", then clearly we should also reverse the truth value.
Or course, a |this| reference might be obscured, for example: () => eval(funcThatReturnsThisString()+".propName")
I think we agree that all fat arrow functions and all functions created using bind have fixed |this| bindings, regardless of whether or not they actually contain a static reference to |this|
From that perspective I think "isThisBound" or just "isBound" is how most people will think about it.
If you want to reverse the sense, I think it would be something like "hasDynamicThis" or perhaps even "isMethod" (where by definition methods would be functions with dynamic |this| bindings).
"canSenseThis" doesn't do a lot me.
Allen Wirfs-Brock wrote:
From that perspective I think "isThisBound" or just "isBound" is how most people will think about it.
I think isBound wins in light of F.p.bind.
If you want to reverse the sense, I think it would be something like "hasDynamicThis" or perhaps even "isMethod" (where by definition methods would be functions with dynamic |this| bindings).
I say isBound still wins cuz now we are mixing metaphors.
"canSenseThis" doesn't do a lot me.
Ditto.
On May 29, 2012, at 9:00 PM, Russell Leggett wrote:
On Tue, May 29, 2012 at 11:52 PM, John Tamplin <jat at google.com> wrote: On Tue, May 29, 2012 at 11:50 PM, Yehuda Katz <wycats at gmail.com> wrote: I'm not sure if this makes sense, but something about breaking
call
andapply
doesn't sit right with me.Why should an arrow function be treated any differently than an explicitly bound function (ie, via bind)?
Hah, you beat me too it, I was typing in almost the exact same thing. And not just bind, either - even the kind of workarounds people would use before bind like 'var self = this' sorts of things. If someone writes code using the arrow, what they want is to use the lexically scoped this - they want to use 'this' just like any other reference captured by the closure.
+1
On May 30, 2012, at 2:18 PM, Mark S. Miller wrote:
However, there might be some utility (at least for debugging) in having a Function.prototype.isThisBound method. It would return true for arrow functions and functions created via Function.prototype.bind
I agree that such a predicate would be useful, but the useful predicate should also give "true" for functions that don't mention "this". Thus, "isThisBound" may not be the right name. What it really means is 'Can this function sense the "this" binding with which it is called?" If we call it "canSenseThis", then clearly we should also reverse the truth value.
I don't understand what this predicate is supposed to test for. What does it return for
function f(x, y) {
if (false) {
console.log(this.foo);
}
return x * y;
}
Can isBound be used to implement Function.prototype.halts? ;
Good catch on the direct eval operator.
I don't care much what we call it or which way the truth value goes, as long as it gives the same answer for
- bound functions
- fat arrow functions
- functions that neither mention "this" nor contain a direct eval operator in their body.
On Wed, May 30, 2012 at 3:17 PM, David Herman <dherman at mozilla.com> wrote:
On May 30, 2012, at 2:18 PM, Mark S. Miller wrote:
However, there might be some utility (at least for debugging) in having a Function.prototype.isThisBound method. It would return true for arrow functions and functions created via Function.prototype.bind
I agree that such a predicate would be useful, but the useful predicate should also give "true" for functions that don't mention "this". Thus, "isThisBound" may not be the right name. What it really means is 'Can this function sense the "this" binding with which it is called?" If we call it "canSenseThis", then clearly we should also reverse the truth value.
I don't understand what this predicate is supposed to test for. What does it return for
function f(x, y) { if (false) { console.log(this.foo); } return x * y; }
Can isBound be used to implement Function.prototype.halts? ;-P
Independent of name, I'm trying to test for "might sense this". Sorry for confusing matters by saying "can". For your f function, I think the answer should be that it might sense this, since it does mention "this" in its body.
On May 30, 2012, at 3:23 PM, Mark S. Miller wrote:
Independent of name, I'm trying to test for "might sense this".
OK, but that begs the question. The problem is that your "might" test is neither sound (as Allen pointed out) nor complete (as I pointed out) for "can." What guarantee are you trying to provide? What do you mean by "might?"
I think the predicate people are asking for is, "does this function behave like a method, and if I use .call/.apply with a custom receiver object, will it use that receiver object?" That's just not something a computable function can accurately predict.
But I haven't read the JSFixed thread Rick mentioned, so I may have misinterpreted. Rick, can you post a link?
It is sound, as corrected in light of Allen's post. If the function contains a direct eval operator in its body, it also "might sense this".
On Wed, May 30, 2012 at 3:31 PM, David Herman <dherman at mozilla.com> wrote:
On May 30, 2012, at 3:23 PM, Mark S. Miller wrote:
Independent of name, I'm trying to test for "might sense this".
OK, but that begs the question. The problem is that your "might" test is neither sound (as Allen pointed out) nor complete (as I pointed out) for "can." What guarantee are you trying to provide? What do you mean by "might?"
I think the predicate people are asking for is, "does this function behave like a method, and if I use .call/.apply with a custom receiver object, will it use that receiver object?" That's just not something a computable function can accurately predict.
Since we have a universal Turing language here, I am certainly not looking for accuracy, I'm looking for soundness. Thus, answering your "if I use .call/.apply with a custom receiver object, will it use that receiver object?" is equally undecidable. If you change your "will" to "might", then we're saying the same thing. The answer "it might" doesn't guarantee much -- hence incompleteness. The answer "it won't" should be a hard guarantee -- hence soundness.
By additionally specifying simple rules that result in the answer "it won't", we enable programmers to predictably write non-this-sensitive functions for which the predicate will vouch for their this-insensitivity.
On Wednesday, May 30, 2012 at 6:31 PM, David Herman wrote:
On May 30, 2012, at 3:23 PM, Mark S. Miller wrote:
Independent of name, I'm trying to test for "might sense this".
OK, but that begs the question. The problem is that your "might" test is neither sound (as Allen pointed out) nor complete (as I pointed out) for "can." What guarantee are you trying to provide? What do you mean by "might?"
I think the predicate people are asking for is, "does this function behave like a method, and if I use .call/.apply with a custom receiver object, will it use that receiver object?" That's just not something a computable function can accurately predict.
But I haven't read the JSFixed thread Rick mentioned, so I may have misinterpreted. Rick, can you post a link?
Sure thing, the discussion was here:
On 31 May 2012 00:31, David Herman <dherman at mozilla.com> wrote:
OK, but that begs the question. The problem is that your "might" test is neither sound (as Allen pointed out) nor complete (as I pointed out) for "can." What guarantee are you trying to provide? What do you mean by "might?"
+1
I think the function that some here seem to want is neither possible, nor desirable. Whether a function is looking at 'this' or not (or how it was constructed that way, i.e. syntactically, or using 'bind') is pretty much an implementation detail, and it should not be possible to sense it. A function is free to document it as part of its contract, though.
On May 31, 2012, at 2:27 AM, Andreas Rossberg wrote:
On 31 May 2012 00:31, David Herman <dherman at mozilla.com> wrote:
OK, but that begs the question. The problem is that your "might" test is neither sound (as Allen pointed out) nor complete (as I pointed out) for "can." What guarantee are you trying to provide? What do you mean by "might?"
+1
I think the function that some here seem to want is neither possible, nor desirable. Whether a function is looking at 'this' or not (or how it was constructed that way, i.e. syntactically, or using 'bind') is pretty much an implementation detail, and it should not be possible to sense it. A function is free to document it as part of its contract, though.
I'm not sure if the last couple sentences above is referring to the suggest isBound methods nor not. Certainly whether a function is defined as an arrow function or created using the bind function isn't an objective fact that can be encoded in the function's representation when it is created.
On 31 May 2012 18:03, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
On May 31, 2012, at 2:27 AM, Andreas Rossberg wrote:
On 31 May 2012 00:31, David Herman <dherman at mozilla.com> wrote:
OK, but that begs the question. The problem is that your "might" test is neither sound (as Allen pointed out) nor complete (as I pointed out) for "can." What guarantee are you trying to provide? What do you mean by "might?"
+1
I think the function that some here seem to want is neither possible, nor desirable. Whether a function is looking at 'this' or not (or how it was constructed that way, i.e. syntactically, or using 'bind') is pretty much an implementation detail, and it should not be possible to sense it. A function is free to document it as part of its contract, though.
I'm not sure if the last couple sentences above is referring to the suggest isBound methods nor not. Certainly whether a function is defined as an arrow function or created using the bind function isn't an objective fact that can be encoded in the function's representation when it is created.
s/isn't/is/ ?
Sure, you can do that, but I can't see how dealing with these cases only would be particularly useful functionality. And regardless, I'd argue that even those are implementation details.
On Thu, May 31, 2012 at 2:27 AM, Andreas Rossberg <rossberg at google.com>wrote:
On 31 May 2012 00:31, David Herman <dherman at mozilla.com> wrote:
OK, but that begs the question. The problem is that your "might" test is neither sound (as Allen pointed out) nor complete (as I pointed out) for "can." What guarantee are you trying to provide? What do you mean by "might?"
+1
I think the function that some here seem to want is neither possible, nor desirable.
While this is impossible to answer for "the function that some here seem to want", since you're responding to Dave who is responding to me, I'll reiterate that the function I want is very possible. As I wrote previously it tests exactly for the following three cases:
On Wed, May 30, 2012 at 3:20 PM, Mark S. Miller <erights at google.com> wrote:
- bound functions
- fat arrow functions
- functions that neither mention "this" nor contain a direct eval operator in their body.
I agree that distinguishing among these three cases would expose irrelevant implementation details. The above three categories are functions that cannot be this-sensitive. AFAICT, this is a hard guarantee. Thus, distinguishing between these three cases together vs all other functions is a semantic distinction.
Whether a function is looking at 'this' or not (or how it was constructed that way, i.e. syntactically, or using 'bind') is pretty much an implementation detail, and it should not be possible to sense it. A function is free to document it as part of its contract, though.
For the code
function f() { if (false) { return this; } }
function g() { }
The point of having rules that predictably say that f might be this-sensitive but g cannot be is so that the author of g can effectively "document" this fact, by having the predicate vouch that g is not this-sensitive, and its customers can know it is not lying. Of course f can document the same claim by conventional means, but it can't document it in a way that assures others it is not lying. Those who use the predicate's answer as their way of "reading" this documentation will only see g's claim, not f's.
See www.erights.org/elang/kernel/auditors, wiki.erights.org/wiki/Guard-based_auditing
None of this should be surprising. We are all familiar with static type systems that will distinguish only between programs that might cause a type error and those that are guaranteed not to. We do not complain that a safe program might need to be revised before a type checker can vouch that it is safe. Likewise, the author of f would need to revise it if they wish the predicate to vouch that it is not this-sensitive.
On Thu, May 31, 2012 at 12:51 PM, Mark S. Miller <erights at google.com> wrote:
We do not complain that a safe program might need to be revised before a type checker can vouch that it is safe.
As the author of a type checker, let me assure you that while you might not complain, your users certainly will.
On 5/31/12 at 2:27, rossberg at google.com (Andreas Rossberg) wrote:
A function is free to document it as part of its contract, though.
Contracts are much more useful if they are enforced by the system, the language and runtime in this case.
Cheers - BIll
Bill Frantz |Security, like correctness, is| Periwinkle (408)356-8506 |not an add-on feature. - Attr-| 16345 Englewood Ave www.pwpconsult.com |ibuted to Andrew Tanenbaum | Los Gatos, CA 95032
On 31 May 2012 19:01, Bill Frantz <frantz at pwpconsult.com> wrote:
On 5/31/12 at 2:27, rossberg at google.com (Andreas Rossberg) wrote:
A function is free to document it as part of its contract, though.
Contracts are much more useful if they are enforced by the system, the language and runtime in this case.
It can already be enforced -- as a callee, do or don't use 'this'; as a caller, do or don't provide a receiver. What an isBound predicate brings to the table is the ability to second-guess the contract, and encourage callers to ignore it in cases where it is not actively enforced by the callee.
On Thu, May 31, 2012 at 1:32 PM, Andreas Rossberg <rossberg at google.com>wrote:
On 31 May 2012 19:01, Bill Frantz <frantz at pwpconsult.com> wrote:
On 5/31/12 at 2:27, rossberg at google.com (Andreas Rossberg) wrote:
A function is free to document it as part of its contract, though.
Contracts are much more useful if they are enforced by the system, the language and runtime in this case.
It can already be enforced -- as a callee, do or don't use 'this'; as a caller, do or don't provide a receiver. What an isBound predicate brings to the table is the ability to second-guess the contract, and encourage callers to ignore it in cases where it is not actively enforced by the callee.
If you reframe the problem as |this| just being another parameter, this whole debate seems quite silly to me. Any function that is written to have a |this| value be dynamic, and possibly set by the caller can be rewritten as a function that does not use |this| and simply has an extra parameter. Framing it this way, asking for an isBound method is no more useful than having a method isParamUsed(n) which checks if the nth parameter is used in the function body.
There are certain cases where a dynamic |this| is a useful way to write that sort of function. If it is being used as a method, or mixed in as a method, that makes sense. The only other case that I don't actually like, but we have to live with is DOM event handlers/jQuery event handlers. Other than that, it just doesn't make sense. Any time a function is passed as an argument and used, there is an implicit contract about what that function should take as inputs, perform as side effects, and return. Whether or not it uses |this| should not really be treated any differently than asking what parameters the function expects/uses.
I think it would lead to brittle code to try to ask the function if it uses |this| as some means of overloading/duck typing. That is too much introspection into the implementation.
On May 31, 2012, at 11:13 AM, Russell Leggett wrote:
If you reframe the problem as |this| just being another parameter, this whole debate seems quite silly to me. Any function that is written to have a |this| value be dynamic, and possibly set by the caller can be rewritten as a function that does not use |this| and simply has an extra parameter. Framing it this way, asking for an isBound method is no more useful than having a method isParamUsed(n) which checks if the nth parameter is used in the function body.
+1
I think it would lead to brittle code to try to ask the function if it uses |this| as some means of overloading/duck typing. That is too much introspection into the implementation.
Beautifully put, thank you. The idea of trying to provide API's that try to infer complicated, uncomputable contracts about the intended use of a function is a fool's errand.
I think there's a core confusion at the heart of this thread. Let's restart operationally.
If one did have such a predicate, when would some other program actually use it? In other words, for the predicate you have in mind, please give a concrete example where a program would usefully say
if (isBoundOrWhateverWeCallIt(f)) {
//... do something
} else {
//... do something else
}
If the predicate means what I think it should mean, I can offer some examples of when I would do this. If the predicate means anything else that's been advocated on this thread, such as "fat arrow or bound", then I have no idea why anyone would ever write code as above. Can anyone offer a concrete example? If not, then I suggest that these other proposed meanings are simply not useful.
On Thu, May 31, 2012 at 5:40 PM, Mark S. Miller <erights at google.com> wrote:
snip
If the predicate means what I think it should mean, I can offer some examples of when I would do this. If the predicate means anything else that's been advocated on this thread, such as "fat arrow or bound", then I have no idea why anyone would ever write code as above. Can anyone offer a concrete example? If not, then I suggest that these other proposed meanings are simply not useful.
Mark,
I'm going to take a stab at this, because I think I have a good understanding of what the JSFixed group is concerned about...
// Imaginary DOM library DOM = {};
// This function accepts a function as it's second argument, // what happens when its a bound function? I'd like write the // API in a way that is friendly to both types of function
DOM.each = function( elems, callbackFn ) { var bound;
// I made this up, but it makes sense because fat arrows don't // have a prototype, so Function.prototype.isBound() is out // if ( !Function.isBound(callbackFn) ) { // When not explicitly bound, // DOM.each sets |this| to the object being iterated bound = callbackFn.bind( elems ); }
[].forEach.call( elems, callbackFn ); }
let nodes = document.querySelectorAll(".theclass");
DOM.each( nodes, function( index ) { // the node is at: this[ index ] });
function ListOfNodes( selector ) { let nodes = document.querySelectorAll( selector );
// I don't want to return a NodeList, I'm // making my own API - WOO!
DOM.each( nodes, index => {
// |this| is the constructed instance
this[ index ] = nodes[ index ];
});
this.length = nodes.length; }
// I might make my own API for // manipulating my ListOfNodes // ListOfNodes.prototype.foo...
var list = new ListOfNodes(".class");
list;
[ div.class, div.class (use imagination) ]
Hopefully this helps!
On May 31, 2012, at 2:40 PM, Mark S. Miller wrote:
I think there's a core confusion at the heart of this thread. Let's restart operationally.
If one did have such a predicate, when would some other program actually use it? In other words, for the predicate you have in mind, please give a concrete example where a program would usefully say
if (isBoundOrWhateverWeCallIt(f)) { //... do something } else { //... do something else }
If the predicate means what I think it should mean, I can offer some examples of when I would do this. If the predicate means anything else that's been advocated on this thread, such as "fat arrow or bound", then I have no idea why anyone would ever write code as above. Can anyone offer a concrete example? If not, then I suggest that these other proposed meanings are simply not useful.
For example, somebody might want to replace the existing apply, call, and bind methods, with versions that throw an error when invoked on a fat arrow or bound function. For example:
(function replaceCall() { if (Function.prototype.hasOwnProperty("intrinsicCall")) return; Object.defineProperty(Function.prototype,"intrinsicCall", {value: Function.prototype.call, configurable:true}); Function.prototype.call = function(...args) { if (this.isBound()) throw new TypeError("Using call method with a bound function"); else return this.intrinsicCall(...args); }; })();
This would seem useful for testing to see if any "bound functions" are being used in situations that may be trying to explicitly provide a this value.
If you are using call anyway and passing the array as thisObj there, why do you need to do the bind at all?
On Thu, May 31, 2012 at 3:08 PM, Rick Waldron <waldron.rick at gmail.com>wrote:
I'm going to take a stab at this, because I think I have a good understanding of what the JSFixed group is concerned about...
Hi Rick, thanks. That's exactly what I was hoping for.
// Imaginary DOM library DOM = {};
// This function accepts a function as it's second argument, // what happens when its a bound function? I'd like write the // API in a way that is friendly to both types of function
DOM.each = function( elems, callbackFn ) { var bound;
// I made this up, but it makes sense because fat arrows don't // have a prototype, so Function.prototype.isBound() is out
fat arrow functions don't have a ".prototype" but they do have a [[Prototype]] of Function.prototype, and so would still inherit Function.prototype.isBound() as an instance method. No matter, I don't really care what the predicate is named, which way the truth value goes, or how it is invoked. So far, this example is fine.
// if ( !Function.isBound(callbackFn) ) { // When not explicitly bound, // DOM.each sets |this| to the object being iterated bound = callbackFn.bind( elems ); }
I don't understand. Here, you're conditionally assigning the newly bound function bound to the "bound" variable. But then you're not using the "bound" variable anywhere. Binding itself has no side effect, so this code seems like a noop. Typo?
On Thu, May 31, 2012 at 3:14 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:
For example, somebody might want to replace the existing apply, call, and bind methods, with versions that throw an error when invoked on a fat arrow or bound function. For example:
(function replaceCall() { if (Function.prototype.hasOwnProperty("intrinsicCall")) return; Object.defineProperty(Function.prototype,"intrinsicCall", {value: Function.prototype.call, configurable:true}); Function.prototype.call = function(...args) { if (this.isBound()) throw new TypeError("Using call method with a bound function"); else return this.intrinsicCall(...args); }; })();
This would seem useful for testing to see if any "bound functions" are being used in situations that may be trying to explicitly provide a this value.
Why does this code not fail the criteria you already offered: it is branching on a non-semantic private implementation decision[1] of the author of the function, and so violating an abstraction boundary?
[1] Prior to .bind(), the std way to lexically bind "this" is to rename it to "that"
var that = this;
addCallback(function() { whatever(that); });
With bind in ES5, we now have the option to say instead
addCallback(function() { whatever(this); }.bind(this));
With fat arrow in ES6, we can also say
addCallback(=> { whatever(this); });
Normally, even without reading the source of addCallback, I'd assume these three bits of code say the same thing[2]. Even if one could write addCallback so that it acts differently in these three cases, isn't that a bad idea?
[2] Except that the third callback cannot be used as a constructor, which thereby more accurately says what we generally take the first two cases to be trying to say[3].
[3] In the future, I'll try to avoid any more nested footnotes ;).
On May 31, 2012, at 4:17 PM, Mark S. Miller wrote:
On Thu, May 31, 2012 at 3:14 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote: For example, somebody might want to replace the existing apply, call, and bind methods, with versions that throw an error when invoked on a fat arrow or bound function. For example:
(function replaceCall() { if (Function.prototype.hasOwnProperty("intrinsicCall")) return; Object.defineProperty(Function.prototype,"intrinsicCall", {value: Function.prototype.call, configurable:true}); Function.prototype.call = function(...args) { if (this.isBound()) throw new TypeError("Using call method with a bound function"); else return this.intrinsicCall(...args); }; })();
This would seem useful for testing to see if any "bound functions" are being used in situations that may be trying to explicitly provide a this value.
Why does this code not fail the criteria you already offered: it is branching on a non-semantic private implementation decision[1] of the author of the function, and so violating an abstraction boundary?
I don't think I argued that for isBound although I certainly would make that argument about arrow functions in general.
In this case, I'm assuming a debugging scenario. Somebody is passing arrow functions through an API whose contract requires a callback function that is invokable with a dynamic this. I'm trying to find the culprit. So I write a function like the above to try to catch it. Or I just put a isBound check in the API function.
It's true, that an offending function might be one that was written with a dynamic this binding but which never actually references this. That is also probably a violation of my contract. So, I may get false negative. But there is still a good change that finding positives will solve my debugging problem.
On Thu, May 31, 2012 at 7:01 PM, Mark S. Miller <erights at google.com> wrote:
snip
// Imaginary DOM library
DOM = {};
// This function accepts a function as it's second argument, // what happens when its a bound function? I'd like write the // API in a way that is friendly to both types of function
DOM.each = function( elems, callbackFn ) { var bound;
// I made this up, but it makes sense because fat arrows don't // have a prototype, so Function.prototype.isBound() is out
fat arrow functions don't have a ".prototype" but they do have a [[Prototype]] of Function.prototype, and so would still inherit Function.prototype.isBound() as an instance method. No matter, I don't really care what the predicate is named, which way the truth value goes, or how it is invoked. So far, this example is fine.
Wouldn't this mean that fat arrow functions inherit call, apply and bind as well? (I may have misunderstood this aspect)
// if ( !Function.isBound(callbackFn) ) { // When not explicitly bound, // DOM.each sets |this| to the object being iterated bound = callbackFn.bind( elems ); }
I don't understand. Here, you're conditionally assigning the newly bound function bound to the "bound" variable. But then you're not using the "bound" variable anywhere. Binding itself has no side effect, so this code seems like a noop. Typo?
Yes, sorry, that is a typo - I put this example together quickly, I should've reviewed it a little more thoroughly. Sorry for the confusion.
On Thu, May 31, 2012 at 4:55 PM, Rick Waldron <waldron.rick at gmail.com>wrote:
Wouldn't this mean that fat arrow functions inherit call, apply and bind as well? (I may have misunderstood this aspect)
I believe they do.
Yes, sorry, that is a typo - I put this example together quickly, I should've reviewed it a little more thoroughly. Sorry for the confusion.
Could you post a corrected example? Thanks.
On Thu, May 31, 2012 at 4:44 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:
I don't think I argued that for isBound although I certainly would make that argument about arrow functions in general.
In this case, I'm assuming a debugging scenario. Somebody is passing arrow functions through an API whose contract requires a callback function that is invokable with a dynamic this. I'm trying to find the culprit. So I write a function like the above to try to catch it. Or I just put a isBound check in the API function.
It's true, that an offending function might be one that was written with a dynamic this binding but which never actually references this. That is also probably a violation of my contract. So, I may get false negative. But there is still a good change that finding positives will solve my debugging problem.
Thanks for the clarification; I'm not sure I would have guessed that from this code.
For the purpose you state, given two isBoundOrWhateverItIsNamed functions f and g where,
- neither has any false positives,
- both obey simple deterministic rules that are easy to understand,
- f has strictly fewer false negatives than g.
Which should you prefer for the purpose of your code?
Mark S. Miller wrote:
On Thu, May 31, 2012 at 4:55 PM, Rick Waldron <waldron.rick at gmail.com <mailto:waldron.rick at gmail.com>> wrote:
Wouldn't this mean that fat arrow functions inherit call, apply and bind as well? (I may have misunderstood this aspect)
I believe they do.
Yes, and both apply and call are useful on arrows, even though |this| cannot be overridden. Arguments still matter ;-).
Wouldn't this mean that fat arrow functions inherit call, apply and bind as well? (I may have misunderstood this aspect)
I believe they do.
Yes, and both apply and call are useful on arrows, even though |this| cannot be overridden. Arguments still matter ;-).
My previous "understanding" seems sort of silly now that I think about it.
On Thu, May 31, 2012 at 8:11 PM, Mark S. Miller <erights at google.com> wrote:
On Thu, May 31, 2012 at 4:55 PM, Rick Waldron <waldron.rick at gmail.com>wrote:
Could you post a corrected example? Thanks.
To reduce any confusion, I've posted the corrected code here: gist.github.com/2847608
On Thu, May 31, 2012 at 5:41 PM, Rick Waldron <waldron.rick at gmail.com>wrote:
On Thu, May 31, 2012 at 8:11 PM, Mark S. Miller <erights at google.com>wrote:
On Thu, May 31, 2012 at 4:55 PM, Rick Waldron <waldron.rick at gmail.com>wrote:
Could you post a corrected example? Thanks.
To reduce any confusion, I've posted the corrected code here: gist.github.com/2847608
Does the code you show (with comments deleted)
DOM.each = function( elems, callbackFn ) { var bound;
if ( !Function.isBound(callbackFn) ) { bound = callbackFn.bind( elems ); } else { bound = callbackFn; }
[].forEach.call( elems, bound ); }
do anything different than the simpler code
DOM.each = function( elems, callbackFn ) { var bound;
bound = callbackFn.bind( elems );
[].forEach.call( elems, bound ); }
?
On May 31, 2012, at 5:31 PM, Brendan Eich wrote:
Mark S. Miller wrote:
On Thu, May 31, 2012 at 4:55 PM, Rick Waldron <waldron.rick at gmail.com <mailto:waldron.rick at gmail.com>> wrote:
Wouldn't this mean that fat arrow functions inherit call, apply and bind as well? (I may have misunderstood this aspect)
I believe they do.
Yes, and both apply and call are useful on arrows, even though |this| cannot be overridden. Arguments still matter ;-).
but they aren't essential and have exact syntactic equivalents
assume f is a fat arrow function
then
f.call(ignored,a,b,c)
is the same as
f(a,b,c)
and
f.apply(ignored,args)
is the same as
f(...args)
I think we want fat arrow functions to support call and apply because people will use them out of habit. But I think we (actually the people who write books, etc.) should encourage use of the syntactic forms whenever passing a this value is irrelevant.
Resending as plain text to avoid the unpredictable mailer-inserted spacing.
On Thu, May 31, 2012 at 5:41 PM, Rick Waldron <waldron.rick at gmail.com> wrote:
On Thu, May 31, 2012 at 8:11 PM, Mark S. Miller <erights at google.com> wrote:
On Thu, May 31, 2012 at 4:55 PM, Rick Waldron <waldron.rick at gmail.com> wrote:
Could you post a corrected example? Thanks.
To reduce any confusion, I've posted the corrected code here: gist.github.com/2847608
Does the code you show (with comments deleted)
DOM.each = function( elems, callbackFn ) { var bound;
if ( !Function.isBound(callbackFn) ) { bound = callbackFn.bind( elems ); } else { bound = callbackFn; }
[].forEach.call( elems, bound ); }
do anything different than the simpler code
DOM.each = function( elems, callbackFn ) { var bound;
bound = callbackFn.bind( elems );
[].forEach.call( elems, bound ); }
?
On May 31, 2012, at 5:18 PM, Mark S. Miller wrote:
On Thu, May 31, 2012 at 4:44 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
I don't think I argued that for isBound although I certainly would make that argument about arrow functions in general.
In this case, I'm assuming a debugging scenario. Somebody is passing arrow functions through an API whose contract requires a callback function that is invokable with a dynamic this. I'm trying to find the culprit. So I write a function like the above to try to catch it. Or I just put a isBound check in the API function.
It's true, that an offending function might be one that was written with a dynamic this binding but which never actually references this. That is also probably a violation of my contract. So, I may get false negative. But there is still a good change that finding positives will solve my debugging problem.
Thanks for the clarification; I'm not sure I would have guessed that from this code.
For the purpose you state, given two isBoundOrWhateverItIsNamed functions f and g where,
- neither has any false positives,
- both obey simple deterministic rules that are easy to understand,
- f has strictly fewer false negatives than g.
Which should you prefer for the purpose of your code?
If I understood it, I would probably prefer what you are proposing. However, I suspect that many JS programs will conceptually equate "bond this" with bound functions and arrow functions and not think about equating it with the concept of "has no dynamic this dependencies". The rules for the later not so simple given that they need to include strict mode and eval conditionals.
On Thursday, May 31, 2012 at 8:49 PM, Mark S. Miller wrote:
On Thu, May 31, 2012 at 5:41 PM, Rick Waldron <waldron.rick at gmail.com (mailto:waldron.rick at gmail.com)> wrote:
On Thu, May 31, 2012 at 8:11 PM, Mark S. Miller <erights at google.com (mailto:erights at google.com)> wrote:
On Thu, May 31, 2012 at 4:55 PM, Rick Waldron <waldron.rick at gmail.com (mailto:waldron.rick at gmail.com)> wrote:
Could you post a corrected example? Thanks.
To reduce any confusion, I've posted the corrected code here: gist.github.com/2847608
Does the code you show (with comments deleted)
DOM.each = function( elems, callbackFn ) { var bound;
if ( !Function.isBound(callbackFn) ) { bound = callbackFn.bind( elems ); } else { bound = callbackFn; }
[].forEach.call( elems, bound ); }
do anything different than the simpler code
DOM.each = function( elems, callbackFn ) { var bound;
bound = callbackFn.bind( elems );
[].forEach.call( elems, bound ); }
The original API allowed me to pass an explicitly bound callback (whether it was by bind or fat arrow) and have that binding take precendence over a default behavior.
I'll be honest, as I wrote this, it did feel contrived.
On Thu, May 31, 2012 at 6:06 PM, Rick Waldron <waldron.rick at gmail.com> wrote:
The original API allowed me to pass an explicitly bound callback (whether it was by bind or fat arrow) and have that binding take precendence over a default behavior.
Hi Rick, I don't understand that sentence. Can you write an example callback, whether contrived on not, that demonstrates any observable difference?
On Thu, May 31, 2012 at 5:56 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
If I understood it, I would probably prefer what you are proposing. However, I suspect that many JS programs will conceptually equate "bond this" with bound functions and arrow functions and not think about equating it with the concept of "has no dynamic this dependencies".
Then let's not give the predicate a confusing name that suggests a less useful meaning.
The rules for the later not so simple given that they need to include strict mode and eval conditionals.
Non-strict mode is so bizzarre that I wouldn't be surprised, but I can't think of an example. Is my proposed rule unsound as stated, since it doesn't distinguish strict and non-strict functions? Is there some way for a non-strict function that doesn't mention "this" and does not contain a direct eval operator to nevertheless be this-sensitive?
On May 31, 2012, at 7:23 PM, Mark S. Miller wrote:
On Thu, May 31, 2012 at 5:56 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
If I understood it, I would probably prefer what you are proposing. However, I suspect that many JS programs will conceptually equate "bond this" with bound functions and arrow functions and not think about equating it with the concept of "has no dynamic this dependencies".
Then let's not give the predicate a confusing name that suggests a less useful meaning.
but if the name emphasis something other than what the program conceptualizes, that is also a problem. I guess it comes down to considering actual names.
The rules for the later not so simple given that they need to include strict mode and eval conditionals.
Non-strict mode is so bizzarre that I wouldn't be surprised, but I can't think of an example. Is my proposed rule unsound as stated, since it doesn't distinguish strict and non-strict functions? Is there some way for a non-strict function that doesn't mention "this" and does not contain a direct eval operator to nevertheless be this-sensitive?
Actually I was probably thinking about pre-ES5 indirect eval which in, at least some implementation, had access to the local scope. So the mode may not be an issue.
In ES6, it could mention "super" so it has to also be included in any analysis.
-----Original Message----- From: es-discuss-bounces at mozilla.org [mailto:es-discuss- bounces at mozilla.org] On Behalf Of Mark S. Miller Sent: Thursday, May 31, 2012 22:24
Non-strict mode is so bizzarre that I wouldn't be surprised, but I can't think of an example. Is my proposed rule unsound as stated, since it doesn't distinguish strict and non-strict functions? Is there some way for a non-strict function that doesn't mention "this" and does not contain a direct eval operator to nevertheless be this-sensitive?
I can't quite find the answer: would setTimeout/setInterval/setImmediate have the capabilities necessary? I would assume they all behave as indirect eval, but since they are outside the language spec, it seems possibly implementation-dependent.
Other candidates are window.execScript in IE or vm.runInNewContext in Node, but I think both of those are explicitly global scoped, and thus could not reference this. Nevertheless, the idea of implementations providing specific methods with the same abilities as direct eval seems possible.
On May 31, 2012, at 6:06 PM, Rick Waldron wrote:
The original API allowed me to pass an explicitly bound callback (whether it was by bind or fat arrow) and have that binding take precendence over a default behavior.
I think you might still be missing a key point: if callbackFn
is a bound function, there's no important difference in behavior between
callbackFn.call(elems, x, i)
and
callbackFn(x, i)
The former passes elems as a this
parameter, but callbackFn
ignores it and uses its own bound this
. The latter doesn't pass a this
parameter, and callbackFn
uses its own bound this
. Either way, you get the same behavior. So you can simply replace lines 9 - 22 with
[].forEach.call( elems, callbackFn );
And there's no need for the isBound predicate.
On May 31, 2012, at 2:40 PM, Mark S. Miller wrote:
if (isBoundOrWhateverWeCallIt(f)) { //... do something } else { //... do something else }
If the predicate means what I think it should mean, I can offer some examples of when I would do this.
Could you? I haven't yet understood what you want your predicate to mean or what you want it for.
FWIW, I tried very hard to come up with an example but failed. If I understand it correctly fat arrow acts is a helper for var self = this
. Then it no matter what the underlying method does the following code will simply ignore this
:
var self = this;
DOM.each("div", function (el) { self.doSomething(); });
Rewriting it using the new syntax:
DOM.each("div", (el) => { this.doSomething(); });
Correct me if I misunderstand how => works.
Anton
On Thu, May 31, 2012 at 10:42 PM, David Herman <dherman at mozilla.com> wrote:
On May 31, 2012, at 6:06 PM, Rick Waldron wrote:
The original API allowed me to pass an explicitly bound callback (whether it was by bind or fat arrow) and have that binding take precendence over a default behavior.
I think you might still be missing a key point: if
callbackFn
is a bound function, there's no important difference in behavior betweencallbackFn.call(elems, x, i)
and
callbackFn(x, i)
The former passes elems as a
this
parameter, butcallbackFn
ignores it and uses its own boundthis
. The latter doesn't pass athis
parameter, andcallbackFn
uses its own boundthis
. Either way, you get the same behavior. So you can simply replace lines 9 - 22 with[].forEach.call( elems, callbackFn );
And there's no need for the isBound predicate.
Yes, you're exactly right - I hadn't realized this would be the behavior -- but it makes perfect sense seeing it from a different perspective.
@Mark - my example no longer holds.
Yes, you're exactly right - I hadn't realized this would be the behavior -- but it makes perfect sense seeing it from a different perspective.
By "different", I mean "correct" ;)
On Thu, May 31, 2012 at 7:44 PM, David Herman <dherman at mozilla.com> wrote:
On May 31, 2012, at 2:40 PM, Mark S. Miller wrote:
if (isBoundOrWhateverWeCallIt(f)) { //... do something } else { //... do something else }
If the predicate means what I think it should mean, I can offer some examples of when I would do this.
Could you? I haven't yet understood what you want your predicate to mean or what you want it for.
Take a look at slides 45 and 46 of www.infoq.com/presentations/Secure-Mashups-in-ECMAScript-5, qconsf.com/dl/qcon-sanfran-2011/slides/MarkS.Miller_RemainingHazardsAndMitigatingPatternsOfSecureMashupsInEcmaScript5.pdf
Rewind first to remind yourself of enough context to get the point.
Rather than teach defensive programmers to write "(1,subscribers[+i])(publication);", I think it is both more robust and more teachable to teach them to do input validation where they accept a function that they expect to not be this-sensitive.
subscribe: function(subscriber) {
if (!isBoundThisOrWhateverWeCallIt(subscriber)) {
throw Error("only non-this-sensitive functions (such as
bound functions) may be subscribers"); } subscribers.push(subscriber); }
Amusingly, the example becomes exactly the inverse of Allen's, leveraging soundness rather than fighting incompleteness.
We could instead make the subscribe method defensive by writing
subscribe: function(subscriber) {
subscribers.push(subscriber.bind(undefined));
}
This would be as safe; it would successfully prevent the same attacks. When the attacks are due to malice, this would be as good -- better since it is simpler. But most "attacks" are actually accidents, not malice. This simpler alternative fails to give an early diagnostic to accidental attackers -- which is directly analogous to the test's purpose in Allen's code. The "(1,subscribers[+i])" technique shown on the slide has the same lack-of-early feedback problem.
With the isBoundThisOrWhateverWeCallIt test as previously proposed, callers of subscribe can only successfully call it with either a fat arrow function or a bound function, which is often an unnecessary burden on these callers. With the test I propose, callers can also successfully call it with any function that neither mentions this nor contains a direct eval operator. The remaining false negatives of my test would cause only the minor annoyance of "unnecessarily" rejecting "safe" cases like
subscribe(function() { if (false) { doSomethingWith(this); } });
related: are explicitly bound functions going to get a 'target' reference to the unbound function in ES6? I've manually set this property many times (mainly for debugging)
On Wed, May 30, 2012 at 2:01 PM, Rick Waldron <waldron.rick at gmail.com>wrote:
a means of detecting bound functions would essentially mitigate any perceived pain points of fat-arrow-functions. If we can get consensus on something like this, it would allay community concerns about the fat arrow.
- It would tell us when we can't change the context via call/apply.
- It would still prevent us from changing the context via call/apply
To my mind (2) is the actual problem. To quote Yehuda "the proposed
semantics interfere with more intentional uses of call
and apply
."
Angus Croll wrote:
To my mind (2) is the actual problem. To quote Yehuda "the proposed semantics interfere with more intentional uses of
call
andapply
."
The followups have made it clear to me there is no such interference in non-error cases. Users of call and apply who need to override |this| have as their contract that any funarg they take has dynamic |this|.
If you want to check to throw a nicer error, that's Mark's use case.
Otherwise there's no way to patch around a function that ignores |this| (or binds it -- but let's stick with ES3 here). Such a function passed as an argument to something like your mixin combinators that use .call is violating your library's contract.
So are you looking for an isBound predicate just to throw an error? I'm not saying that is a non-goal, I'm just asking to find out if there's some other, non-throwing recourse your code could use.
On Jun 2, 2012, at 23:14, Brendan Eich <brendan at mozilla.org> wrote:
Angus Croll wrote:
To my mind (2) is the actual problem. To quote Yehuda "the proposed semantics interfere with more intentional uses of
call
andapply
."Users of call and apply who need to override |this| have as their contract that any funarg they take has dynamic |this|.
Yes of course, can't argue with that logic, or your earlier explanation of why modern engines can't be expected to have call/apply modify a hard binding.
But that's exactly why we should be conservative about locking users into hard bindings when their intentions are not clear (clear: 'bind' or no |this| value, unclear: arrow functions). As someone who views call/apply as cornerstones of the language and who's libraries depend on it, an unintended hard binding is a needlessly broken utility. But I repeat myself.
So are you looking for an isBound predicate just to throw an error? I'm not saying that is a non-goal, I'm just asking to find out if there's some other, non-throwing recourse your code could use.
I'm not looking for an isBpund predicate - not sure the dividends would be worth the effort. If the hard-binding is non-negotiable then it would be more useful (and instructive for the user) for call/apply to tell me why it won't change my context - I'd rather it throw an error* than have our utils silently fail because the user was unaware of the semantics of =>.
*If the intention is to use call/apply purely as an argument passer this can be indicated by a null context argument which would suppress the error
On Jun 2, 2012, at 23:14, Brendan Eich <brendan at mozilla.org> wrote:
So are you looking for an isBound predicate just to throw an error?
Sorry misread your question. Yes. For reasons stated.
Angus Croll wrote:
On Jun 2, 2012, at 23:14, Brendan Eich<brendan at mozilla.org> wrote:
Angus Croll wrote:
To my mind (2) is the actual problem. To quote Yehuda "the proposed semantics interfere with more intentional uses of
call
andapply
." Users of call and apply who need to override |this| have as their contract that any funarg they take has dynamic |this|.Yes of course, can't argue with that logic, or your earlier explanation of why modern engines can't be expected to have call/apply modify a hard binding.
Ok.
But that's exactly why we should be conservative about locking users into hard bindings when their intentions are not clear (clear: 'bind' or no |this| value, unclear: arrow functions). As someone who views call/apply as cornerstones of the language and who's libraries depend on it, an unintended hard binding is a needlessly broken utility. But I repeat myself.
Are you arguing for -> instead of =>, or in addition to =>?
There's nothing particularly "unclear" about a function that doesn't use |this| or else binds |this|, vs. a function that uses its dynamic |this|. Clarity depends on many things including how well documented or commented the code -- especially that contract that we just agreed exists -- might be.
So why are arrow functions "unclear"? Asserting that they are doesn't prove the point. It seems to me you're assuming too many developers won't learn that => means lexical |this|, and that they'll continue not
to learn.
*If the intention is to use call/apply purely as an argument passer this can be indicated by a null context argument which would suppress the error
This is an incompatible change if done for any function that ignores the |thisArg|:
js> function f() { var self = this; return function () { return
self.foo; } } js> var g = f()
js> var o = {m: f, foo: "o.foo"}
js> var h = o.m();
js> var foo = "global foo"
js> g.apply(null) "global foo" js> h.apply(null) "o.foo" js> g.apply({foo: "new foo"}) "global foo" js> h.apply({foo: "new foo"}) "o.foo"
Same for ES5 bound functions:
js> function unb() { return this.foo; }
js> var b = unb.bind({foo: "b.foo"})
js> b.apply(null) "b.foo" js> b.apply({foo: "new foo"}) "b.foo"
Why should only arrow functions, among all functions that ignore |thisArg|, throw when applied with a non-null |thisArg|?
Brendan Eich wrote:
Clarity depends on many things including how well documented or commented the code -- especially that contract that we just agreed exists -- might be.
One thing I forgot to write:
With array extras such as forEach, almost always the contract is local and needs no docs or comments. Indeed people write
function outerMethod() { ... anArray.forEach(function (elem) { ... this /* oops */ ... }); ... }
IIRC this was an error from real life that tomvc showed to the list.
Implicit, local contracts where lexical |this| is required are exactly the use-case for =>. Kevin Smith's analysis, although it studied only a
few codebases and isn't some kind of definitive empirical study, suggests that this use-case, combined with those where functions don't use |this| at all, dominate.
But now I'm repeating myself. The point here was that the contract is often local and even implicit in the sense that JS hackers, even experienced ones, fall into the trap.
As with most programming errors, there's no perfect defense in style or language restriction short of avoiding |this| altogether. But there's good clarity based on the local contract and implicitly lexical |this| in the above code rewritten to use =>:
function outerMethod() { ... anArray.forEach(elem => { ... this /* yay! */ ... }); ... }
I'm sorry this doesn't extend to your mixin library. If we had -> then
your users who want shorter syntax would have a choice, but that choice creates the option to choose wrongly, and the extra complexity combined with the common-case analyses from Kevin and others caused us to add only => to ES6 as proposed.
But that's exactly why we should be conservative about locking users into
hard bindings when their intentions are not clear (clear: 'bind' or no |this| value, unclear: arrow functions). As someone who views call/apply as cornerstones of the language and who's libraries depend on it, an unintended hard binding is a needlessly broken utility. But I repeat myself.
Are you arguing for -> instead of =>, or in addition to =>?
At this point I'd settle for anything that allowed both abbreviated syntax and late binding via call/apply. That could be any of:
- Arrow function that shortens syntax but leaves semantics alone
- Arrow function with soft lexical binding
- Thin arrow as no-semantic alternative to fat arrow (coffee script style)
Looks like (3) has the most chance of gaining acceptance - that would work for me.
There's nothing particularly "unclear" about a function that doesn't use |this| or else binds |this|, vs. a function that uses its dynamic |this|. Clarity depends on many things including how well documented or commented the code -- especially that contract that we just agreed exists -- might be.
So why are arrow functions "unclear"? Asserting that they are doesn't prove the point. It seems to me you're assuming too many developers won't learn that => means lexical |this|, and that they'll continue not to learn.
Its impossible for me to prove they are unclear just as its impossible for you to prove they are clear. I can only:
-
point to the syntax and suggest 'bind' says exactly what it does while '=>' says nothing about what it does
-
point to unscientific and noisy issue comments at jsfixed and note that at least some experienced JS devs share my concerns re. lack of clarity
JSFixed/JSFixed#42, JSFixed/JSFixed#20
Also note that here, several fine developers got into a mess with figuring out the impact on 'this' in fat arrows
But I readily admit that I won't know how valid my concerns are until the syntax starts being used in anger.
*If the intention is to use call/apply purely as an argument passer this
can be indicated by a null context argument which would suppress the error
This is an incompatible change if done for any function that ignores the |thisArg|:
js> function f() { var self = this; return function () { return self.foo; } } js> var g = f() js> var o = {m: f, foo: "o.foo"} js> var h = o.m(); js> var foo = "global foo" js> g.apply(null) "global foo" js> h.apply(null) "o.foo" js> g.apply({foo: "new foo"}) "global foo" js> h.apply({foo: "new foo"}) "o.foo"
Same for ES5 bound functions:
js> function unb() { return this.foo; } js> var b = unb.bind({foo: "b.foo"}) js> b.apply(null) "b.foo" js> b.apply({foo: "new foo"}) "b.foo"
Why should only arrow functions, among all functions that ignore |thisArg|, throw when applied with a non-null |thisArg|?
Not suggesting that arrow functions be special cased or that they ignore |thisArg|. I'm suggesting for all relevant cases (=>, bind and no |this|)
we only throw an error on call/apply if the |thisArg| is non null.
Angus Croll wrote:
But that's exactly why we should be conservative about locking users into hard bindings when their intentions are not clear (clear: 'bind' or no |this| value, unclear: arrow functions). As someone who views call/apply as cornerstones of the language and who's libraries depend on it, an unintended hard binding is a needlessly broken utility. But I repeat myself. Are you arguing for -> instead of =>, or in addition to =>?
At this point I'd settle for anything that allowed both abbreviated syntax and late binding via call/apply. That could be any of:
- Arrow function that shortens syntax but leaves semantics alone
- Arrow function with soft lexical binding
- Thin arrow as no-semantic alternative to fat arrow (coffee script style)
Looks like (3) has the most chance of gaining acceptance - that would work for me.
(1) has to be -> or we'll get lynched by CoffeeScripters and anyone who
sees the precedent there.
You agreed (2) ain't gonna happen, so let's drop it.
That indeed leaves (3) but it's a hard sell right now. I'll feel out members of the committee. It could happen, don't get me wrong, but as we only just got => in via cutting complexity including having two kinds of
arrows to choose from, one of which lands common 40-50% use cases back in the dynamic |this| trap, trying to re-inject -> will require careful
argumentation.
*If the intention is to use call/apply purely as an argument passer this can be indicated by a null context argument which would suppress the error This is an incompatible change if done for any function that ignores the |thisArg|: js> function f() { var self = this; return function () { return self.foo; } } js> var g = f() js> var o = {m: f, foo: "o.foo"} js> var h = o.m(); js> var foo = "global foo" js> g.apply(null) "global foo" js> h.apply(null) "o.foo" js> g.apply({foo: "new foo"}) "global foo" js> h.apply({foo: "new foo"}) "o.foo" Same for ES5 bound functions: js> function unb() { return this.foo; } js> var b = unb.bind({foo: "b.foo"}) js> b.apply(null) "b.foo" js> b.apply({foo: "new foo"}) "b.foo" Why should only arrow functions, among all functions that ignore |thisArg|, throw when applied with a non-null |thisArg|?
Not suggesting that arrow functions be special cased or that they ignore |thisArg|. I'm suggesting for all relevant cases (=>, bind and no |this|) we only throw an error on call/apply if the |thisArg| is non null.
We can't do that, though:
-
It's backward-incompatible, breaking 1JS with a runtime-only shift in semantics.
-
It's hard to decide what a function that doesn't use |this| actually is. Mark proposes a conservative approximation but you'd get false positives throws. This is arguably ok for an isBound predicate, less so for apply and call special-casing (ignoring 1).
What's more, I don't see why this matters now given apply and call dating from '99 and ES3, and functions that don't use |this| (including the pattern used to implement bind in the language itself) existing all this time. I haven't heard anyone asking for an error when trying to pass a non-null first arg to .apply/call on a function that doesn't use |this|, until now.
There could be a problem that did not rise to anyone's attention till lately, or possibly it was not diagnosed, merely latent. But is it possible that this is a non-issue, because people generally satisfy the |this|-contract of APIs they use, especially where the contract requires dynamic-|this|? Rather it's the other side of the coin, where lexical-|this| is assumed, that has risen over the years as a pain point showing dynamic-|this| can be a footgun.
But I agree the coin has two sides. IIRC from talking with @jashkenas, he couldn't tell which side was heavier by frequency of use, so wanted two-char arrows. He went for => as "fat arrow" to convey the greater
overhead of a self-hosted bound lexical-|this| function compared to "thin arrow".
I'm in favor of both and wrote strawman:arrow_function_syntax to spec both. I'll keep working on ->, but not without arguments that
attend to the ones leading up to ES6 getting => this past March.
On 3 June 2012 20:13, Angus Croll <anguscroll at gmail.com> wrote:
Not suggesting that arrow functions be special cased or that they ignore |thisArg|. I'm suggesting for all relevant cases (=>, bind and no |this|) we only throw an error on call/apply if the |thisArg| is non null.
That would not only be a breaking change, as Brendan pointed out. More importantly, it would also not be something you truly want. Consider this piece of code:
function C(x) { this.x = x } C.prototype.f = function() { return this.x } C.prototype.g = function() { return this.f() * 2 } C.prototype.h = function() { return 41 }
let o = new C(21) foo(o, [o.f, o.g, o.h])
where
function foo(o, funs) { // ... for (let f of values(funs)) { result += f.call(o) } }
According to your suggestion, foo would throw. But there is no actual reason why foo should care that h is a constant method. That's an implementation detail. Why should the caller have to jump through hoops to account for implementation details of the callee?
I think there is a misconception here is that this-oblivious methods/functions are something different from "normal" methods/functions. They aren't. They are just a special case of normal methods/functions. Just like functions that ignore any other parameter. Consequently, you don't usually want to treat them differently. I claim that code that tries has a 98% likelihood of being broken -- or at least of smelling rather bad.
I actually agree. I included non-this methods only because reading through this thread I got a feeling that there was some consensus that they be treated differently. More than happy to not do that.
Angus Croll wrote:
I actually agree. I included non-this methods only because reading through this thread I got a feeling that there was some consensus that they be treated differently. More than happy to not do that.
You mean you still propose that (=> 42).call({}) should throw, but (function () { return 42; }).call({}) should not?
Sorry, this isn't a principled response. There should be no difference in result between those two cases.
Apart from principles, it seems to me you think arrows will be such sweet sugar that people will make mistakes using this-sensitive API contracts, where long-but-this-insensitve-functions would not be so misused.
To demonstrate this we need to see some evidence. It's not enough to worry, or to hypothesize unfixed code shipped and failing at scale due to failure to test arrow function misuse on the part of developers.
On Mon, Jun 4, 2012 at 9:54 AM, Brendan Eich <brendan at mozilla.com> wrote:
You mean you still propose that (=> 42).call({}) should throw, but (function () { return 42; }).call({}) should not?
Sorry, this isn't a principled response. There should be no difference in result between those two cases.
I disagree. You yourself said "so are you looking for an isBound predicate just to throw an error? I'm not saying that is a non-goal". The principal is let the user know they are trying to set |this| in a hard bound function. The level of introspection required to go the extra mile and look for |this| usage seems neither desirable or useful. If my function doen't reference |this| then I don't care if |this| is modifiable.
However I thought we had some agreement that -> was a valid goal. In which
case this error throwing stuff is less important (to me at least)
Apart from principles, it seems to me you think arrows will be such sweet sugar that people will make mistakes using this-sensitive API contracts, where long-but-this-insensitve-**functions would not be so misused.
To demonstrate this we need to see some evidence. It's not enough to worry, or to hypothesize unfixed code shipped and failing at scale due to failure to test arrow function misuse on the part of developers.
I'm the first to admit I have no hard evidence, only heresay
To be honest, I was mostly thinking about this feature from a jQuery transition perspective.
Right now, callbacks to methods like each
in jQuery expect to receive a
this
binding for the current element in the iteration.
For example, in the case of each
, the call signature looks like this:
jQuery.each(someArray, function(i, item) { // this and item are the items in the Array // i is the index });
It's too late for jQuery to fix the order now. However, in the face of
arrows or bound functions, we probably could detect a user intent to use
this
for their own purposes and use that as a signal to change the order.
jQuery.each = function(array, callback) { for (var i=0, l=array.length; i<l; i++) { item = array[i]; if (isBound(callback)) { callback(item, i); } else { callback.call(item, i, item); } } };
I don't know whether it makes sense for a brand-new API to be designed this
way, but for APIs that presently make (possibly misguided) use of this
in
place of a first parameter, the new (user-initiated, not available in ES3)
binding behavior provides a single shot at a better way forward.
Yehuda Katz (ph) 718.877.1325
Angus Croll wrote:
On Mon, Jun 4, 2012 at 9:54 AM, Brendan Eich <brendan at mozilla.com <mailto:brendan at mozilla.com>> wrote:
You mean you still propose that (=> 42).call({}) should throw, but (function () { return 42; }).call({}) should not? Sorry, this isn't a principled response. There should be no difference in result between those two cases.
I disagree. You yourself said "so are you looking for an isBound predicate just to throw an error? I'm not saying that is a non-goal".
There's no isBound test in the above, though.
The topic was an incompatible change to .apply and .call where the left of dot is some (not all) bound-this/no-this forms and the arg-list to the right starts with a non-null |thisArg|.
Compatibility says we can't change the bound (as in ES5 bind) and no-this cases (pre-ES5, e.g. PrototypeJS bind, or var self=this;... self open coded solutions) behavior. Ok, but you're right, arrows are new, we could throw on non-null |thisArg| presented to someArrow.call or.apply.
But the example I showed is just a refactoring (shortening or lengthening). There's no isBound test, no use of .prototype, etc. That's why i wrote "in principle". If you change to a different apples vs. oranges (or apples vs. apples) setting, principles may differ.
The principal is let the user know they are trying to set |this| in a hard bound function.
That's not a general principle, though. Andreas called out functions that ignore one parameter among N, vs. those that do not. We don't have an "isParameterUsed(name)" magic predicate on functions that can take a formal parameter name and tell us the same thing you want for |this| in the form of isBound().
Not that there's anything wrong with isBound -- the point is by what principle do you distinguish |this| from other parameters that could be ignored?
The level of introspection required to go the extra mile and look for |this| usage seems neither desirable or useful.
Sorry, not sure I followed this sentence. Do you mean by "level of introspection" the pain of Function.prototype.toString searching, say using a regexp, to see if any "this" word occurs? Or more complete: a full parse of function source?
If so, I get it and agree.
If my function doen't reference |this| then I don't care if |this| is modifiable.
I agree but why doesn't this apply to arrows as well? The issue is not whether the callee ignores or binds |this|, it is whether the caller expects to override |this| and it has a callee that doesn't care (in which case a thrown error could be better than proceeding). Or so I thought you were saying!
However I thought we had some agreement that -> was a valid goal. In which case this error throwing stuff is less important (to me at least)
You and I agree on -> but (a) it's not in ES6 and not actively proposed; (b) how does this relieve the concern that people will pass arrows (or other bound-this and no-this functions) to an API whose contract requires dynamic |this|?
Yehuda Katz wrote:
I don't know whether it makes sense for a brand-new API to be designed this way, but for APIs that presently make (possibly misguided) use of
this
in place of a first parameter, the new (user-initiated, not available in ES3) binding behavior provides a single shot at a better way forward.
Yeah, this is a variation on Mark's use-case and I think a Function.prototype.isBound (or better name) should be considered.
On Monday, June 4, 2012 at 3:10 PM, Yehuda Katz wrote:
It's too late for jQuery to fix the order now. However, in the face of arrows or bound functions, we probably could detect a user intent to use
this
for their own purposes and use that as a signal to change the order.
If I read the code correctly, it means that the same method will have different signatures depending on the function form: $("").each(function (i, item) { … }); vs. $("").each((item, i) => { … })
If so, I think it will be very confusing.
Anton
On Mon, Jun 4, 2012 at 6:10 PM, Yehuda Katz <wycats at gmail.com> wrote:
snip
jQuery.each(someArray, function(i, item) { // this and item are the items in the Array // i is the index });
It's too late for jQuery to fix the order now. However, in the face of arrows or bound functions, we probably could detect a user intent to use
this
for their own purposes and use that as a signal to change the order.jQuery.each = function(array, callback) { for (var i=0, l=array.length; i<l; i++) { item = array[i]; if (isBound(callback)) { callback(item, i); } else { callback.call(item, i, item); } } };
As a relevant aside - If you're not referring to the current item of the iterating list via |this|, you could just as easily use fat arrow w/ jQuery.each() today: gist.github.com/2871510
Rick
I don't know whether it makes sense for a brand-new API to be designed this
On Monday, June 4, 2012 at 6:56 PM, Anton Kovalyov wrote:
If I read the code correctly, it means that the same method will have different signatures depending on the function form: $("").each(function (i, item) { … }); vs. $("").each((item, i) => { … })
I can assure you that will never actually happen - Yehuda was merely illustrating a use case.
Either way - this is way OT for this mail list.
On 5 June 2012 00:56, Anton Kovalyov <ml at kovalyov.net> wrote:
On Monday, June 4, 2012 at 3:10 PM, Yehuda Katz wrote:
It's too late for jQuery to fix the order now. However, in the face of arrows or bound functions, we probably could detect a user intent to use
this
for their own purposes and use that as a signal to change the order.If I read the code correctly, it means that the same method will have different signatures depending on the function form: $("").each(function (i, item) { … }); vs. $("").each((item, i) => { … })
If so, I think it will be very confusing.
+1. If anything, I view this example as an argument in favour of not providing isBound.
On Mon, Jun 4, 2012 at 12:54 PM, Brendan Eich <brendan at mozilla.com> wrote:
Angus Croll wrote:
I actually agree. I included non-this methods only because reading through this thread I got a feeling that there was some consensus that they be treated differently. More than happy to not do that.
You mean you still propose that (=> 42).call({}) should throw, but (function () { return 42; }).call({}) should not?
Sorry, this isn't a principled response. There should be no difference in result between those two cases.
And the other cases where this is bound should be treated identically:
var self = this; return function () { self....; }
return function () { this...; }.bind(this);
return => this...;
Aside from the last, these cases already are options that could be passed to an API that expects to use this as a hidden parameter. Why should the last be treated differently than the other two?
I don't object to having a non-bound arrow, but aside from jQuery's (IMHO) misuse of this, the bound-this case seems to be the most prevalent and the least likely to confuse people.
Andreas Rossberg wrote:
On 5 June 2012 00:56, Anton Kovalyov<ml at kovalyov.net> wrote:
On Monday, June 4, 2012 at 3:10 PM, Yehuda Katz wrote:
It's too late for jQuery to fix the order now. However, in the face of arrows or bound functions, we probably could detect a user intent to use
this
for their own purposes and use that as a signal to change the order.If I read the code correctly, it means that the same method will have different signatures depending on the function form: $("").each(function (i, item) { … }); vs. $("").each((item, i) => { … })
If so, I think it will be very confusing.
+1. If anything, I view this example as an argument in favour of not providing isBound.
I see that argument. I wrote recently that isBound should be considered, and we're doing that. Perhaps Mark's use-case justifies adding it, but that is kind of an expert feature.
What I perceive from the JSFixed effort, and from Angus who is good enough to post here: people have a particular concern that fat-arrow is too sweet and it will lure the children into the witch's house: passing fat arrows to dynamic-this APIs.
We have data suggesting that fat arrows address the dominant use-case, thanks to Kevin Smith and others. So fat arrows are in ES6, well and good.
I think the particular concern about => being an attractive nuisance for
some APIs such as Angus's mixin combinators, which rely on .call overriding |this|, can be addressed by adding -> too. Angus agrees, but -> is not on the boards for ES6 (yet).
We could try to revive ->, but first, we should face the attractive
nuisance argument squarely, instead of dancing around it with isBound abuses that try to "catch fat arrow going into the witch's house".
On Tue, Jun 5, 2012 at 12:36 PM, Brendan Eich <brendan at mozilla.com> wrote:
What I perceive from the JSFixed effort, and from Angus who is good enough to post here: people have a particular concern that fat-arrow is too sweet and it will lure the children into the witch's house: passing fat arrows to dynamic-this APIs.
We have data suggesting that fat arrows address the dominant use-case, thanks to Kevin Smith and others. So fat arrows are in ES6, well and good.
I think the particular concern about => being an attractive nuisance for some APIs such as Angus's mixin combinators, which rely on .call overriding |this|, can be addressed by adding -> too. Angus agrees, but -> is not on the boards for ES6 (yet).
We could try to revive ->, but first, we should face the attractive nuisance argument squarely, instead of dancing around it with isBound abuses that try to "catch fat arrow going into the witch's house".
I think that with ->, a similar problem will still crop up - specifically
that => will be the more common use, and then in the rare case that -> is
needed, people may still use =>. Do we still need isBound to catch that
error as well? Even with the choice of -> or =>, the person writing the
code has to know which one to use and why. That means they have to understand the whole dynamic |this| problem. I think that the dynamic |this| behavior of jQuery is not something that should be encouraged. I understand it is probably mostly that way because of the dom event api, but that doesn't change the fact that it really has a bad smell to it. CoffeeScript has ->, but if you look at the examples, none of them actually
make use of dynamic |this| except for methods, and we're adding a nice method syntax, so it isn't really needed. Method syntax, and => should
cover the majority of cases and lead people down the right path.
I know this is going around in a circle, but my point is that adding ->
doesn't fix the problem, which is devs not knowing when to use => and when
to use function.
Russell Leggett wrote:
On Tue, Jun 5, 2012 at 12:36 PM, Brendan Eich <brendan at mozilla.com <mailto:brendan at mozilla.com>> wrote:
What I perceive from the JSFixed effort, and from Angus who is good enough to post here: people have a particular concern that fat-arrow is too sweet and it will lure the children into the witch's house: passing fat arrows to dynamic-this APIs. We have data suggesting that fat arrows address the dominant use-case, thanks to Kevin Smith and others. So fat arrows are in ES6, well and good. I think the particular concern about => being an attractive nuisance for some APIs such as Angus's mixin combinators, which rely on .call overriding |this|, can be addressed by adding -> too. Angus agrees, but -> is not on the boards for ES6 (yet). We could try to revive ->, but first, we should face the attractive nuisance argument squarely, instead of dancing around it with isBound abuses that try to "catch fat arrow going into the witch's house".
I think that with ->, a similar problem will still crop up - specifically that => will be the more common use, and then in the rare case that -> is needed, people may still use =>.
I agree. The problem we'll then see is anxiety over "which arrow?"
On Tue, Jun 5, 2012 at 12:56 PM, Russell Leggett <russell.leggett at gmail.com>wrote:
I think that the dynamic |this| behavior of jQuery is not something that should be encouraged. I understand it is probably mostly that way because of the dom event api, but that doesn't change the fact that it really has a bad smell to it.
+1
Yes the thread needs wrapping up. Maybe I can attempt summarize the dilemma
- with a question:
Is call/apply just a remedy for non-lexical this assignment? Or is it a powerful feature in it own right.
I'm with the second camp, but I think I'm in the minority in this list
Angus Croll wrote:
Yes the thread needs wrapping up. Maybe I can attempt summarize the dilemma - with a question:
Is call/apply just a remedy for non-lexical this assignment? Or is it a powerful feature in it own right.
I'm with the second camp, but I think I'm in the minority in this list
The second, of course :-)
Angus Croll wrote:
Is call/apply just a remedy for non-lexical this assignment? Or is it a powerful feature in it own right.
This isn't a well-defined question. Do you mean "powerful" as in, must be able to override |this| via the |thisArg| first parameter?
We know that's not possible even in ES3, did that make call/apply "less powerful"? Hard to believe since they were added in ES3.
If not, does adding arrows then somehow "reduce" the power of call/apply? How?
Anyway, call/apply is not "just a remedy" for non-lexical |this| assignment (dynamic |this| binding other than by capturing the callable as a method in the receiving object). ES3 added call/apply to support invocation given an explict thisArg, and in particular added apply to support passing actual args in an array or arguments object instead of positionally.
I fear we're not wrapping up if we are talking about "power". This isn't post-structural literary analysis, ya know!
On Tue, Jun 5, 2012 at 2:30 PM, Angus Croll <anguscroll at gmail.com> wrote:
Yes the thread needs wrapping up. Maybe I can attempt summarize the dilemma - with a question:
Is call/apply just a remedy for non-lexical this assignment? Or is it a powerful feature in it own right.
I'm with the second camp, but I think I'm in the minority in this list
Call/apply are of course powerful features, but they should not be used just for the sake of using them. You know what's cleaner looking than call/apply? Just calling the function directly!
I think the bigger, but related question is, "When should dynamic |this| be used?" Do you think the jQuery style of using the dynamic this in callback is good? Many of us here do not. Dynamic |this| allows for functions to work like methods - it enables mixins and all kinds of meta-programming goodness. However, it can also be abused, and create really funky code that can lead to weird surprises.
Maybe I'm just conservative here. I suppose I can see the appeal of not having to add that pesky extra parameter and instead use |this| for whatever arbitrary thing you might want to put there, but it sure seems less readable to me. You might as well just use the arguments object for everything instead having parameter names!
Until now, with the addition of bind, and the fat arrow, there were a lot of common cases where you would have to put a callback method back with its original |this| object. That is one use of call/apply, and one that will hopefully need less use. Another nice use of it was to easily delegate by using apply and passing through the arguments object. That will go away with rest parameters. So yeah, we might be seeing less of call/apply with ES6, but I think we're replacing them with better patterns instead of just saying they aren't useful anymore.
I would also like to express that I think => with proposed (bound this) semantics, are great. And I don't think it introduces any new issues it's just a sugar for:
function() { /* … */ }.bind(this)
-- Irakli Gozalishvili Web: www.jeditoolkit.com
Irakli Gozalishvili wrote:
I would also like to express that I think => with proposed (bound this) semantics, are great. And I don't think it introduces any new issues it's just a sugar for:
function() { /* … */ }.bind(this)
Not quite, since we do not want to call bind -- so it would be unobservable and you couldn't replace Function.prototype.bind with your bind-like and have the VM call your replacement.
Also, arrows do not have exactly the same internal and external properties that the bound function created by ES5 15.3.4.5 gets, in particular [[Construct]]. See the strawman and possibly the latest ES6 draft for details.
HTH, and not to take away from your endorsement of => at all -- thanks! :-)
I've been thinking a lot about the proposed arrows and its affect on
this
.To start, I'm a worried that
call
andapply
don't "work" on arrow function. This is of course the correct behavior for jQuery-like use-cases, but the proposed semantics interfere with more intentional uses ofcall
andapply
.I'd like to propose the following:
call
andapply
modifythis
in an arrow functionsoftCall
andsoftApply
do not modifythis
in arrow functions, but do modifythis
in regular functionsThis would allow libraries like jQuery to continue providing advisory
this
bindings without interfering with intentional modifications to thethis
binding. (jQuery could dosoftCall = Function.prototype.softCall || Function.prototype.call
to quickly "polyfill").I'm not sure if this makes sense, but something about breaking
call
andapply
doesn't sit right with me.Thoughs?
Yehuda Katz (ph) 718.877.1325