How else might you bind arrow functions?

It’s a bit silly that you can’t bind an arrow function. Let’s get around that:

Function.prototype.arrowBind = function(self){
    return eval(this.toString().replace('this', 'self'))
}

(x => x * this).arrowBind(4)(11) // 44

Other than actually parsing out the proper this using an AST instead of a regular expression, how could this be improved? I feel like I may be missing another trick.

Changing the lexical this

Function.prototype.arrowBind = function(self){
    return function(f){
        return eval(f.toString())
    }.call(self, this)
}
2 Likes

Ah, yes, that’s a much nicer way. I was playing with lexicality in a much messier way than in your approach.

Any idea if there’s a way to escape using eval / toString? I suspect not, but hey you never know. (Not including some cheap hack that amounts to using eval. )

Not with arrow functions. I can’t see any other way to do it. Not unless you’re babeling to ES5 where you’re ultimately getting standard function expressions. Even then you’d have to monkey patch bind.

Too bad. I think Function#bind should work with arrow functions. I like the terseness of arrow functions, and I don’t think their existence should be entangled with the security-minded crowd who came up with ideas like the Secure ECMAScript Subset or Google Caja to enforce within-program data privacy.

It seems weird that some random API would accept a function f, and whether or not it could successfully bind was essentially a stylistic preference at the call site. I’d prefer => be bindable by default, with some sort of Object.freezeBind (or whatever) that would cause the current behavior. You’d still get lexical this by default, but programs could override with bind. And override override with freezeBind. And override override override with some even more meta API like Object.meta( { freezeBind: () => void 0 }).

Of course it’s too late and the ship has already sailed, but hey. :2c: :two_cents: (?)

At first I didn’t like the un-bindable nature of arrow functions, but I kind of like it now - the guarantee that the block will maintain its context no matter how its used… of course our arrowBind shows that such a guarantee is not entirely dependable. Then again, I see your point. Why restrict? Use the default behavior for most circumstances, but for those that require that extra push over the cliff, allow the rebinding. Certainly there are going to be situations where a different context may be desired or expected for a function, and does it make sense to disallow the leaner arrow functions to be used in those cases?

I guess it doesn’t bother me now since any place where having the leaner syntax really makes a difference (one liners) this is hardly ever referenced.

I guess that’s one of the consequences of the "this is an explicit first parameter" philosophy of most OO languages… that this is considered part of the context, even though (in JS) sometimes it can be swapped out.

Recently I’ve been using Julia a bunch (not my favorite language ever), and it eschews the whole this/self concept entirely and just has super prolific type-based method overloading for everything. Like an array push is push!(my_array, my_new_element).

I tend to be the jerk who uses multiline () => s wherever feasible. I’ve even done the reverse of the let self = this thing that arrows were meant to fix. :trout:

Ooo Julia has call overloading. I wish JS had that sometimes.

I guess I’d be ok with the type overloading approach. I think that gets passed some of the ambiguity you get with dynamic contexts. I think if you asked me 5 years ago, I’d immediately tell you I hate it.

But I think I’m more receptive to these kinds of things now, especially as long as tooling properly supports type based autocompletion and whatnot. I can see it getting a little ugly, though.

I had a bug (which I detected very quickly) which surfaced due to too much multiple dispatch. Basically I passed a string representing an airport code into something expecting a geo coordinate, so "KIRUPA" would eventually be turned into a LatLong of [75.0, 73.0]… because you can do “array access” on strings, and there’s an automatically attempted conversion from Char to Float64 at some point.

It’s how they get away with having optional/gradual types. But then when you want to use first-class functions, you have to specify the type, like code_llvm(f, (Complex,)) or code_llvm(f, (Int64,))

f(x) = x + x

code_llvm(f, (Int64,))
# define i64 @julia_f_1638(i64){
# top: 
#   %1 = sh1 i64 %0, 1
#   ret i64 %1
# }

Yeah, you end up dumping a lot into a really flat namespace a la PHP and friends, but at least there’s import/using/export and there aren’t many stdlib functions imported by default (except random math stuff).

Sort of sucks at the start of a line, when autocomplete typically isn’t up by default. And by virtue of things not starting with my_circle.radius, it’s a little hard to guess what my_circle is capable of via any autocomplete. (E.g. you’d type ra and get back something like random(n::Float64), random_int(), racecar(c::Car), ...... ... . ... radius(c::Circle))

Yeah I was thinking about how exactly that would work after I posted. Given some object of a type, could you get a list of functions with a first parameter matching that type? I guess that could be a way to go, but then you’re starting a line with an expectation that auto complete will prefix the line with the proper call which is presumptuous. And PHP immediately jumped in my head with the push example. Though if I remember right (has it been that long since I used PHP?) I think PHP tends to prefix their method names with a type, e.g. array_push.