Post by Peter Cooper on January 16th, 2007
Closures in Ruby

- Getting down with Closures, Blocks, and Procs
- A presentation of Idiomatic Ruby
- Videos from The Ruby Hoedown Now Online


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.

Click here to add on del.icio.us
Tweet This









January 16th, 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 16th, 2007 at 5:44 pm
That technique is also sometimes used with ERb to support 'return' in ERb templates.
January 17th, 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 17th, 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