Procs, Lambdas, Blocks?! What's the Difference?

Do you know?
I didn’t. And started to get annoyed by using these terms interchangeably and not really knowing the difference.

There are a few. And they are subtle. I don’t think most of us would ever have problems with it but it’s the kind of information you’ll be glad to know when having those weird behaviors in your code on a Friday at 6:01 pm, just before pushing to production. :)

I guess blocks are the most widely used term in the ruby community and there is little mistake on when to use it:

[1,2,3].each do |x|
puts x*2
end

The code between do and end is a block.

What’s important to keep in mind is that Procs behave like blocks whereas lambdas behave like methods. To understand what that means, I highlighted a couple of examples:

- The return keyword

I mentioned Procs behave just like blocks and as such, the return keyword abide to the same rules. This means, for instance, that the latest puts statement on the following code snippet, will never run:
def my_proc(x)
p = Proc.new { puts x*2; return }
p.call
puts "After calling proc" #This never gets called
end
my_proc(10)
>>20

For blocks - and procs-, return means “return from the calling method”, my_proc in this case. That’s why you don’t get to see the output of the puts statement.

On the other hand, in the lambda’s example, we get the opposite behavior:
def my_lambda(x)
p = lambda { puts x*2; return }
p.call
puts "After calling proc" #This time, we reach this point
end
my_lambda(10)
>>20
>>After calling proc

Here, return says “return from the enclosing iterator”, which, in this case, just returns from the block and continnues the execution of the my_lambda method.

- Argument assignment

On to this second difference, procs and lambdas get more interesting when you can call them with arguments. And that’s when another subtle difference between them comes in.

I’ll start again with a proc:


p = Proc.new { |x,y| puts x, y}
p.call
>>nil
>>nil

p.call(1)
>>1
>>nil

p.call(1,2)
>>1
>>2

p.call(1,2,3)
>>1
>>2

p.call([1,2])
>>1
>>2


See how procs are flexible? They basically won’t complain if you do not provide parameters, provide extra parameters or even send an array as an argument where, as seen in the code above, it unpacks the array and assign its values to the correct variables.

As you’re probably guessing, lambdas behave like methods and are much less flexible:


l = lambda { |x,y| puts x, y}
l.call
>>ArgumentError: wrong number of arguments (0 for 2)

l.call(1)
>>ArgumentError: wrong number of arguments (1 for 2)

l.call(1,2)
>>1
>>2

l.call(1,2,3)
>>ArgumentError: wrong number of arguments (3 for 2)

l.call([1,2])
>>ArgumentError: wrong number of arguments (1 for 2)




Ruby 1.9 tip
Despite its name, Kernel.proc returns a lambda in Ruby 1.8. This has been fixed in Ruby 1.9. You actually get a Proc back.

- Reference
The Ruby Programming Language - A must have for any Ruby developer.




Comments