arrows and a proposed softCall

Brendan Eich brendan at mozilla.com
Sun Jun 3 16:23:09 PDT 2012


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:
>
> 1) Arrow function that shortens syntax but leaves semantics alone
> 2) Arrow function with soft lexical binding
> 3) 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:

1. It's backward-incompatible, breaking 1JS with a runtime-only shift in 
semantics.

2. 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 
http://wiki.ecmascript.org/doku.php?id=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.

/be


More information about the es-discuss mailing list