5 ms·
First-class functions have the `this` problem in every language, and the fat arrow is very nice syntactic sugar for what you have to do anyway Not true. JavaSc
by wycats 15y ago
First-class functions have the `this` problem in every language, and the fat arrow is very nice syntactic sugar for what you have to do anyway
Not true. JavaScript's `this` binding is absolutely not the only way to do it.
In Ruby:
class Foo
def hello
return proc { self }
end
end
puts Foo.new.hello.call #=> #<Foo:0x10e56c840>
In Python:
class Foo(object):
def hello(self):
def zoo():
return self
return zoo
zoo = Foo().hello()
print(zoo()) #=> <__main__.Foo object at 0x102453f90>
In Ruby, blocks carry the original `self` as part of their variable scope. Rubinius exposes this if you want to poke around:
ruby-1.9.3-head :007 > x = proc {}
=> #<Proc:0x1598@(irb):7>
ruby-1.9.3-head :008 > x.block.scope.self
=> main
In Python, it's a side-effect of the fact that you need to explicitly define `self` as a parameter.
In Ruby, it's possible to override the `self` in a block using instance_eval, just as it's possible to explicitly define a `this` in JavaScript using `.call`.
The difference is that in JavaScript, the callee is always responsible for deciding what the `this` should be (often implicitly), where in Ruby and Python, the function's original binding includes its `self`.
edit: another way to put it would be that Coffee's fat arrow fix is the default in Ruby and Python.
- cheald 15y agoYou're right - but I'd argue that the difference is just in default behavior, as you touched on in your edit. Ruby procs are bound to their lexical scope, and implicitly include self as a part of that scope. The function still has to be aware of some scope to run, though. (I didn't know about assigning self in instance_eval, though - that makes me all kinds of happy!) What I meant to convey is that every language that uses first class functions (I guess I should say "any language with first class functions and the concept of this/self") has to deal with the problem of execution context. Languages like Ruby and Python assume the common case and bundle the context by default. Languages like Javascript and Lua don't, leaving it up to the caller to explicitly specify. In all cases, though, the callee has to be made aware of some context, which is what I meant by the `this` problem. My point is that since Javascript requires explicit binding, a language construct that allows Ruby-style implicit binding is nice syntactic sugar. The author seemed to be conveying the idea that using jQuery's $.proxy magically freed him from the need to pass context around, which is just simply not the case.
- premchai21 15y agoWhat do you mean by Lua leaving the caller to explicitly specify? Are you referring to earlier versions of Lua that used the explicit ^ upvalue sigil? Lua 5.1 (and 5.2) functions close over all local variables (including the implicit “self” introduced by function definitions with colon syntax), with the innermost ones first and no explicit upvalue sigil, much like Scheme.
- cheald 15y agoNah; regular old Lua 5.1. The colon syntax is just syntactic sugar -- self isn't actually bound. function Foo:Bar(baz) is the same as Foo.Bar = function(self, baz); invoking Foo:Bar("rebar") is sugar for Foo.Bar(Foo, "rebar"). self is never bound - it's just passed in (explicitly, via . syntax, or implicitly, vs : syntax). In all cases, the caller is always specified. You can pass Foo.Bar around (as it's a function reference), but if you have something like: Foo = {} function Foo:Bar(baz) print(baz) end local baz = Foo.Bar Then baz has no binding information to Foo; defining the function with the : syntax is just syntactic sugar. To invoke, you would have to call: baz(Foo, "woohoo") Just calling baz("woohoo") populates self with "woohoo", and the bar parameter would be nil, demonstrating that there is no contextual binding to the function itself.
- premchai21 15y agoWhat do you mean “isn't actually bound”? The function being defined using colon syntax doesn't close over self, since it's a parameter—but functions defined within that function will close over the self parameter, since it's a local from an enclosing scope: foo = { x = 3 } function foo:bar(baz) return function(thud) return thud + baz + self.x end end womble = foo:bar(4) womble(7) --> 14 Python I believe also closes over self as a variable, and Ruby has similar behavior for local procs, even though « self » is a special form in Ruby. This is in distinct contrast to JavaScript, where the value of the special form « this » goes nuts inside closures because it's attached to the function: foo = { x: 3 }; foo.bar = function(baz) { return function(thud) { return thud + baz + this.x; }; }; womble = foo.bar(4); womble(7) # --> NaN So in what way are you placing Lua and JavaScript together, and Python and Ruby together? [slightly rearranged for clarity]
- jashkenas 15y agoSo, this is really interesting... JavaScript is forced to make the pessimal choice of only ever having dynamic-bound "this", because of that favorite whipping boy ... prototypal inheritance. If JavaScript had a notion of a class (even as a constructor function + prototype) definition, then it would be possible to tag methods with the correct instance reference, as in Ruby and in Python. Unfortunately, because the standard pattern for defining an instance method is: Klass.prototype.method = function() { ... }; ... where the function is an expression just like any other, you have to have dynamic-bound this to make prototypes work correctly by default.
- jballanc 15y agoYou should look at Lua. Yes, if you want a notion of "self" or "this" with prototypal inheritance, then it must be dynamically bound. However, this does not have to be a burden on, or even be noticeable by, the programmer. Good language design can avoid most of the pitfalls in JavaScript...
- extension 15y agoTo elaborate, Lua has special syntax for defining and calling methods vs unbound functions. In Lua, function woot:donk() ... end woot:donk() is equivalent to woot.donk = function(self) ... end woot.donk(woot) and when you define a function normally e.g. function() ... end there is no self parameter, thus the one from the parent scope is captured. The flaw with JS, really, is that it forces every function to be a method, and doesn't let you express whether you are defining one or the other. (think I got this right, my Lua is a bit rusty)
- LeafStorm 15y agoYep, that's pretty much it. And the woot:donk() notation will only evaluate what's left of the colon once, which is nice.
- judofyr 15y agoThis isn't really about prototypal inheritance. If you wrote methods like this in Ruby, you would have the same issue: Klass.method := lambda { p self } Also, if JavaScript looked like this, you wouldn't need the fat arrow: // Let's call this a "prototype function" Klass.prototype.function method() { … } Together with these rules: * A function always captures its outer `this` * The getter of a prototype function (`thing.method`) always returns a binded function (where `this` == `thing`).