Ruby: a Block, Proc, and Lambda walk into a bar…

Steve K
4 min readOct 19, 2020

This week's post will be a short walkthrough on reducing code by using built-in methods for Ruby using Lambda’s, and Proc’s this came from a need to try to always refactor into a Rubyists solution once I complete anything. So for anyone relatively new in basic terms, these methods are ways to group code you want to run.

# Block Examples[1,2,3].each { |x| puts x*2 } # block is in between the curly braces[1,2,3].each do |x|
puts x*2 # block is everything between the do and end
end
# Proc Examples
p = Proc.new { |x| puts x*2 }
[1,2,3].each(&p) # The ‘&’ tells ruby to turn the proc into a block
proc = Proc.new { puts “Hello World” }
proc.call # The body of the Proc object gets executed when called
# Lambda Examples
lam = lambda { |x| puts x*2 }
[1,2,3].each(&lam)
lam = lambda { puts “Hello World” }
lam.call

The above snippet shows each one in action. At first, it may look like I just showed you two versions of the same thing with different syntax. This is true but the subtlety of the syntax differences to have reasons for it that we will cover now.

Procs and Blocks

So to start this off the most apparent distinction here is that Procs are objects.

p = Proc.new { puts “Hello World” }

With this assignment, we can call methods and assign variables to it and the Proc will always return itself. This is important because blocks are just syntax on a method call. It has no meaning by itself and will only appear in argument lists.

def multiple_procs(proc1, proc2)
proc1.call
proc2.call
end
a = Proc.new { puts "First proc" }
b = Proc.new { puts "Second proc" }
multiple_procs(a,b)

Another feature of procs is that you can pass multiple procs to a method. While only a single block can appear in a list of arguments. These two key takeaways are what set Procs and Blocks apart. This is also when possible you should consider using a Proc to clean up recursion.

Procs and Lambdas

Moving on now this is where differences are smaller but remain very impactful with how each one is used. An important detail and a bit of a gotcha are to understand that both Lambdas and Procs are Proc objects.

proc = Proc.new { puts “Hello world” }
lam = lambda { puts “Hello World” }
proc.class # returns ‘Proc’
lam.class # returns ‘Proc’

The difference comes in that lambdas are a bit of a subset or category/flavor of procs. This distinction happens when returning objects.

proc # returns ‘#<Proc:0x007f96b1032d30@(irb):75>’
lam # returns ‘<Proc:0x007f96b1b41938@(irb):76 (lambda)>’

The notation above is a reminder that while both are very similar and both are instances of the Proc class there is a key difference.

lam = lambda { |x| puts x }    # creates a lambda that takes 1 argument
lam.call(2) # prints out 2
lam.call # ArgumentError: wrong number of arguments (0 for 1)
lam.call(1,2,3) # ArgumentError: wrong number of arguments (3 for 1)

Lambdas check arguments as well as the amount that is supplied. This may seem insignificant at first but procs do not care if they are passed the incorrect amount of arguments.

proc = Proc.new { |x| puts x } # creates a proc that takes 1 argument
proc.call(2) # prints out 2
proc.call # returns nil
proc.call(1,2,3) # prints out 1 and forgets about the extra arguments

To elaborate on this more both Lambdas and Procs treat the return keyword different. Inside a lambda ‘return’ will execute the code outside the lambda snippet.

def lambda_test
lam = lambda { return }
lam.call
puts "Hello world"
end
lambda_test # calling lambda_test prints 'Hello World'

Whereas return inside a proc triggers logic outside the method where the execution of the proc is occurring.

def proc_test
proc = Proc.new { return }
proc.call
puts “Hello world”
end
proc_test # calling proc_test prints nothing

So finishing up while the similarities are abundant the few differences for both Lambdas and Procs are enough to have you evaluate which best suits your current need. Sometimes it's irrelevant how many arguments are supplied in the return you are looking for and that is perfect for a Proc whereas you may want a specific execution within a dataset on some key objects and a Lambda would suit you better. The biggest takeaway I’ve learned is to always evaluate what you are returning. At least in development less code means it's more flexible and reusable in general. More code means more errors or use cases to think about.

--

--