Creating and Invoking a Block

Problem

You want to put some Ruby code into an object so you can pass it around and call it later.

Solution

By this time, you should familiar with a block as some Ruby code enclosed in curly brackets. You might think it possible to define a block object as follows:

aBlock = { |x| puts x } # WRONG # SyntaxError: compile error

That doesn't work because a block is only valid Ruby syntax when it's an argument to a method call. There are several equivalent methods that take a block and return it as an object. The most favored method is Kernel# lambda:[3]

[3] The name lambda comes from the lambda calculus (a mathematical formal system) via Lisp.

aBlock = lambda { |x| puts x } # RIGHT

To call the block, use the call method:

aBlock.call "Hello World!" # Hello World!

 

Discussion

The ability to assign a bit of Ruby code to a variable is very powerful. It lets you write general frameworks and plug in specific pieces of code at the crucial points.

As you'll find out in Recipe 7.2, you can accept a block as an argument to a method by prepending & to the argument name. This way, you can write your own trivial version of the lambda method:

def my_lambda(&aBlock) aBlock end b = my_lambda { puts "Hello World My Way!" } b.call # Hello World My Way!

A newly defined block is actually a Proc object.

b.class # => Proc

You can also initialize blocks with the Proc constructor or the method Kernel#proc. The methods Kernel#lambda, Kernel#proc, and Proc.new all do basically the same thing. These three lines of code are nearly equivalent:

aBlock = Proc.new { |x| puts x } aBlock = proc { |x| puts x } aBlock = lambda { |x| puts x }

What's the difference? Kernel#lambda is the preferred way of creating block objects, because it gives you block objects that act more like Ruby methods. Consider what happens when you call a block with the wrong number of arguments:

add_lambda = lambda { |x,y| x + y } add_lambda.call(4) # ArgumentError: wrong number of arguments (1 for 2) add_lambda.call(4,5,6) # ArgumentError: wrong number of arguments (3 for 2)

A block created with lambda acts like a Ruby method. If you don't specify the right number of arguments, you can't call the block. But a block created with Proc.new acts like the anonymous code block you pass into a method like Enumerable#each:

add_procnew = Proc.new { |x,y| x + y } add_procnew.call(4) # TypeError: nil can't be coerced into Fixnum add_procnew.call(4,5,6) # => 9

If you don't specify enough arguments when you call the block, the rest of the arguments are given nil. If you specify too many arguments, the extra arguments are ignored. Unless you want this kind of behavior, use lambda.

In Ruby 1.8, Kernel#proc acts like Kernel#lambda. In Ruby 1.9, Kernel#proc acts like Proc.new, as better befits its name.

See Also

Категории