Thursday, September 9, 2010

Ruby Procs

I've just started learning Ruby in conjunction with the Ruby on Rails framework. Going through my first Rails tutorial, I found myself trying to understand the respond_to code in my generated controllers. This lead me to an examination of Ruby's block and proc constructs. While experimenting I discovered that there's at least two ways to declare blocks, and three ways to instantiate Ruby Procs. Counting the 2 block variations, you may see 6 permutations of Ruby Proc instantiation.


strings = ["Foo", "Bar", "Blah"];

# Below is 6 functionally similar first-class Procs defined in different ways.
procs = []
procs.push(lambda {|x| print x})
procs.push(lambda do |x| print x end)
procs.push(Proc.new {|x| print x})
procs.push(Proc.new do |x| print x end)
procs.push(proc{|x| print x})
procs.push(proc do |x| print x end)

procs.each {|i| puts i}
# Prints:
#<Proc:0x0000010086a8c0@blocks_and_procs.rb:5 (lambda)>
#<Proc:0x0000010086a898@blocks_and_procs.rb:6 (lambda)>
#<Proc:0x0000010086a870@blocks_and_procs.rb:7>
#<Proc:0x0000010086a848@blocks_and_procs.rb:8>
#<Proc:0x0000010086a820@blocks_and_procs.rb:9>
#<Proc:0x0000010086a7f8@blocks_and_procs.rb:10>

for i in 0..5
print "strings.map of proc #{i}: "
strings.map &procs[i]
print "\n"
end
# Prints
#strings.map of proc 0: FooBarBlah
#strings.map of proc 1: FooBarBlah
#strings.map of proc 2: FooBarBlah
#strings.map of proc 3: FooBarBlah
#strings.map of proc 4: FooBarBlah
#strings.map of proc 5: FooBarBlah

# And here's the inline methods of passing a block.
print "strings.map of do block:"
strings.map do |x| print x end
print "\n"

print "strings.map of {} block:"
strings.map {|x| print x}
print "\n"
# Prints
#strings.map of do block:FooBarBlah
#strings.map of {} block:FooBarBlah


You may notice that a dump of my procs yields two instances which note they're lambdas. Lambda and Proc.new have subtle differences in their argument checking and the way returns are handled from the Proc. See this link for more detail.

Admittedly, I couldn't help but be a little disappointed that there's so many ways to accomplish the same simple thing with Ruby. Particularly when one of its prized attributes is a syntax that is simple and consistent.

No comments: