Closures in Ruby
Bruce Tate continues his fine Crossing Borders series with a look at Ruby's support for closures. If code blocks and block techniques used by routines such as Rails' respond_to confuse you, it's a great primer.
Bruce Tate continues his fine Crossing Borders series with a look at Ruby's support for closures. If code blocks and block techniques used by routines such as Rails' respond_to confuse you, it's a great primer.
January 16, 2007 at 5:38 pm
Interesting link, but one caution for anyone about to read it:
The author presents the 'def fun(&b); yield; end' style as being an anonymous function, which it isn't; For instance, a return in the closure will kill it. Instead, for example, 'def fun(&b); (lambda &b).call; end' will allow full closures, complete with returns.
January 16, 2007 at 5:44 pm
That technique is also sometimes used with ERb to support 'return' in ERb templates.
January 17, 2007 at 8:21 am
I think he's missing a major point of closures: the fact that they can refer to variables in the definition's surrounding scope even after the function in which they are defined has returned. In Javascript, this is very natural:
function make_adder(x) {
return function(y) { return x + y; };
}
var add_ten = make_adder(10);
alert(add_ten(1)); /* Displays 11 */
As has been mentioned, this is a little awkward in Ruby:
def make_adder(x)
return Proc.new {|y| x + y}
# Or
return lambda {|y| x + y }
# Or
return lambda {|y| return x + y}
# But not!
return Proc.new{|y| return x + y}
# This gives an error 'unexpected return', because the function
# this Proc is returning from is not itself, but make_adder, which is not
# on the stack anymore
end
add_ten = make_adder(10)
puts add_ten.call(1) # Displays 11
puts add_ten(1) # Gives an error
The requirement to use the call method makes it not as transparent as it should be, since closures are (or should be) simply functions, like in Javascript (or any Lisp)
January 17, 2007 at 11:52 pm
you can use [] brackets to call a proc as well. so
puts add_ten.call(1) # Displays 11
puts add_ten(1) # Gives an error
puts add_ten[1] # Displays 11