SeldomBlog

Artist. Musician. Programmer.

Blocks vs. Procs vs. Lambdas: Ruby Closure Showdown

Early on in your quest to learn Ruby, you’re going to encounter ‘blocks’. These little packets of code that can be passed to methods may make you sweat a little at first, but soon enough, passing blocks to Enumerable instance methods like ‘each’ and ‘map’ will be second nature. Then, one day, you’ll encounter (gulp), a ‘proc’, or (bigger gulp), a ‘lambda’, and you’ll question why you ever got into this coding business in the first place. Luckily, you can cast those doubts aside, because despite the intimidating names, procs and lambdas are really just blocks stored as variables. Let’s take a minute to break this love triangle apart and examine the subtle differences between blocks, procs and lambdas.

Blocks

Blocks are packets of code stored between ‘do…end’ or ’{}’. They’re a way of performing an action on a value returned by a method.

Define and call a method that yields to a block
1
2
3
4
5
6
def my_method
  yield
end

my_method {"This is a block!"}
=> "This is a block!"
The example above returns “This is a block!”. The method ‘my_method’ yields to the block, which then executes it’s code.

(It actually returns ‘nil’ because of the puts statement, but we’re going to ignore that in this and future examples)

Procs

A Proc is just a block that is stored in a variable for repeated use. If we store a proc in a variable, we can pass that variable to any method that expects a proc.

Store a block in a variable as a Proc
1
2
3
4
5
6
7
8
def proc_method(a_proc)
  puts a_proc.call
end

my_proc = Proc.new{"This is a proc!"}

proc_method(my_proc)
=> "This is a proc!"
If we want to reuse the block of code stored in the my_proc variable, we can simply define another method that takes a proc and pass in my_proc.

Pass the same proc to another method
1
2
3
4
5
6
def another_proc_method(a_proc)
  puts a_proc.call + " - reused!"
end

another_proc_method(my_proc)
=> "This is a proc! -  reused!"

Lambdas

Lambdas are very similar to procs, only with a slightly different syntax.

Store a block in a variable as a Lambda
1
2
3
4
5
6
7
8
def lambda_method(a_lambda)
  puts a_lambda.call
end

my_lambda = lambda {"This is a lambda!"}

lambda_method(my_lambda)
=> "This is a lambda!"
So, lambdas are like procs. In fact, if we run my_lambda.class, we’ll see that it returns ‘Proc’ - that’s right, lambdas are instances of the Proc class. Now pick your jaw up off the floor and let’s go over the differences between the two.

Procs vs. Lambdas

Procs and lambdas behave in slightly different ways with regards to a) argument handling and b) return context. Lambdas, sticklers that they are, are very particular with arguments, and will raise an error if passed less or more arguments than were set when they were defined. Procs, on the other hand, couldn’t give two bits how many arguments you pass them - a proc will set all unused arguments to nil.

Parameter handling with procs and lambdas
1
2
3
4
5
6
7
8
9
10
11
def parameter_handling(proc_or_lambda)
  proc_or_lambda.call(1)
end

my_proc = Proc.new {|a,b| "Procs aren't sticklers about args..."}
my_lambda = lambda {|a,b| "...but Lambdas are!"}

parameter_handling(my_proc)
=> "Procs aren't sticklers about args..."
parameter_handling(my_lambda)
=> <ArgumentError: wrong number of arguments (1 for 2)>
So, if you want your reusable block to require specific amount of arguments, you should use a lambda instead of a proc.

The other way procs and lambdas differ is in return context. When a proc encounters a return statement, it will execute the proc and exit the wrapping method. On the other hand, lambdas, ever the anal retentive counterpart to the freewheeling proc, will execute the lambda and then politely continue to run the wrapping method. Let’s take a look at an example.

Return context with procs and lambdas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def return_test
  puts "beginning"
  Proc.new{return puts "middle"}.call
  puts "end"
end

return_test
=> "beginning"
   "middle"

def return_test
  puts "beginning"
  lambda {return puts "middle"}.call
  puts "end"
end

return_test
=> "beginning"
   "middle"
   "end"
As you can see, when returning our proc, we exited the wrapping method (hence the absence of an “end”), while in returning our lambda, we executed the lambda code and then continued executing the wrapping method call.

Conclusion

So procs and lambdas are really just two ways of storing blocks in variables for repeated use. Admittedly, our examples were pretty elementary, but blocks, procs and lambdas can be actually be incredibly powerful. When passed to a method call, they can act on dynamically generated variables (!), making them all ‘Closures’, which is a topic way outside the scope of this post. For now, just think of procs and lambdas as the the odd couple spawn of the all mighty block.

(Note: This post was inspired by a short and very informative presentation on the subject available on speakerdeck. Highly recommended for a more in depth look at use cases of procs and lambdas. Many thanks to the author ; )