DEV Community

Renata Marques
Renata Marques

Posted on • Edited on

Mastering Ruby: Understanding Blocks, Procs, and Lambdas

First, let's do a little introduction.

Ruby blocks are anonymous functions that can be passed into methods, a way of grouping statements, are passed to methods that yield them within the do and end keywords, and they can have multiple arguments.

A Proc object is an encapsulation of a block of code, which can be stored in a local variable, passed to a method or another Proc, and can be called.

Lambdas are anonymous functions, objects of the class Proc, they are useful in most of the situations where you would use a proc.

So, procs are blocks, and lambdas are procs, that's because all of them are closures, and they have similar characteristics thanks for this. What is a closure in Ruby? A closure is basically a function that:

  1. Can be treated as a variable, i.e. assigned to another variable, passed as a method argument, etc.
  2. Remembers the values of all the variables that were in scope when the function was defined and is able to access these variables even if it is executed in a different scope.

However, what really sets them apart?

What is the difference between Block, Proc, and Lambda?

First, Proc and lambda are objects but blocks are not.

Second, a lambda checks the number of arguments passed to it, while a proc does not.
This means that lambda will throw an error if you pass it the wrong number of arguments, whereas a proc will ignore unexpected arguments and assign nil to any that are missing.

Block uses the keyword yield, yield is a Ruby keyword that calls a block when you use it, the code inside the block will run and do its work.

Like in the example below, they all are Ruby blocks:

def print_message
  yield
end
print_message { puts "Block message" }

[1,2,3,4].each { |i| puts i }

[1,2,3,4].each do |i|
  puts i
end
Enter fullscreen mode Exit fullscreen mode

Third,
when a lambda returns, it passes control back to the calling method,
when a proc returns, it does immediately, without going back to the calling method.

Like in the example below:

def best_movie_proc
  movie = Proc.new { return "Harry Potter!" }
  movie.call
  "Lord of the rings!"
end

puts best_movie_proc # prints "Harry Potter!"

def best_movie_lambda
  movie = lambda { return "Harry Potter!" }
  movie.call
  "Lord of the rings!"
end

puts best_movie_lambda # prints "Lord of the rings!"
Enter fullscreen mode Exit fullscreen mode

See how the proc says "Harry Potter!", this is because it returns immediately, without going back to the best_movie_proc method.

Our lambda, however, goes back into the method after being called, so the method returns the last code it evaluates: "Lord of the rings!"

Calls

Block call

def method
  if block_given?
    yield
  end
end

method { puts "Block message" }
Enter fullscreen mode Exit fullscreen mode

In the given example, we have a block passed with yield keyword.

Proc call

def method(proc)
  proc.call # or proc.()
end

my_proc = proc { puts "Proc message" }

method(my_proc)
Enter fullscreen mode Exit fullscreen mode

In procs, we have two different syntaxes, with call and .()

Lambda call

def method(lambda)
  lambda.call # or lambda.()
end

my_lambda = -> { puts "Lambda message" }

method(my_lambda)
Enter fullscreen mode Exit fullscreen mode

Because lambdas are procs, they also have the same call syntax, but they are different to create.

Conclusion

I hope it has become clearer for you to harness the power of Ruby closures.

Top comments (1)

Collapse
 
ramseslopez profile image
Ramsés López

Excellent explanation! thanks for sharing