Getting down with Closures, Blocks, and Procs
Paul Cantrell has created a cool guide, with demonstrations, of the powers, quirks, and surprises of closures, blocks, and procs in Ruby. He writes:
A closure is a block of code which meets three criteria:
* It can be passed around as a value and
* executed on demand by anyone who has that value, at which time
* it can refer to variables from the context in which it was created (i.e. it is closed with respect to variable access).
Closures are a mainstay of functional languages, but are present in many other languages as well (e.g. Java's anonymous inner classes). You can do cool stuff with them: they allow deferred execution, and some elegant tricks of style.
Ruby is based on the "principle of least surprise," but I had a really nasty surprise in my learning process. When I understood what methods like "each" were doing, I thought, "Aha! Ruby has closures!" But then I found out that a function can't accept multiple blocks -- violating the principle that closures can be passed around freely as values. This document details what I learned in my quest to figure out what the deal is.
All but the highest Ruby masters are going to find at least one thing in here that makes them scratch their head for a few seconds and go "Ah, yes!".
He walks all the way through closure and block land through to generating a Lisp style enumerable class with lazy evaluation. Language geeks, enjoy!
July 1, 2006 at 7:21 pm
What is it with all those closure types anyway? It seems like I should be able to straightforward things like...
a_closure = {|arg| 10 * arg}
a_closure(5) #=> 50
[1,2,3].map(a_closure) #=> [10, 20, 30]
Is this impossible to reconcile with the rest of ruby's syntax?
July 5, 2006 at 6:03 pm
Probably not impossible to reconcile with the syntax, but rather with the implementation: CRuby does a bunch of madness with setjump / longjump to make blocks run fast, and relies on static syntax information to do it.
The reason not to have only one type of block is that you want this to work:
def find_elem
@elements.each do |x|
return x if yield x
end
end
If there are only lambdas, then the "return" on line three can't break out of the "each." So Ruby at least needs the two control flow alternatives.
The rest of the distinctions seem unnecessary to me as well.