Code Blocks, Procs, Lambdas, and Closures in Ruby

Code Blocks, Procs, Lambdas, and Closures in Ruby

Code blocks and other members of the closure family are something near to many Rubyists’ hearts. They are extremely powerful, flexible, and, when used well, elegant. They are the cornerstones of Ruby’s functional style of programming.

In this series, we will take a look at procs, code blocks, lambdas, and closures in Ruby and explore the differences between them and how to use them.

I was afraid of all these names for a long time. But after taking a really close look at each one of them and their relationships to each other, I realized they were not that scary at all. Instead, I promise you, they are really fun subjects.


This is part 1 of a three-part series.

Part 1 covers the following four fundamentals:

  1. Definitions of Code Block, Proc, Lambda, and Closure
  2. Constructions
  3. Calling a Code Block/Proc/Lambda
  4. Passing and Returning Procs

 

Part 2 covers

  • Scopes, Universes, and Lunch Boxes
  • Differences between a Proc and a Lambda

 

Part 3 covers:

  • Proc <> Code Block Conversion and Ampersand(&)

Here is the meat for today.

1. Definitions

  • A code block is a block of code. Sometimes, people simply call a code block a block.
  • A proc is an object that contains a code block. It provides a way to save up a code block and execute it later.
  • A lambda is a special kind of proc (more on that later).
  • A closure is a function that: 1. can be passed around as a variable and 2. binds to the same scope in which it was created (more on that later).

 

To different extents, code blocks, procs, and lambdas can be seen as closures. Closure is a higher-level concept compared to Code Block, Proc,and Lambda.

Yup, it’s dead simple and straightforward.

 

2. Constructions

There are two different ways to construct each of them.

  • Code Block: { } OR do end
  • Proc: Proc.new OR proc followed by a code block
  • Lambda: lambda OR -> (stabby lambda) followed by a code block
  • Closure: again, Code blocks, procs, and lambdas can be seen as different forms of closures.

 

# Code Block
#1. {} Mostly used as a one-liner
{ puts 'Hi' }
#2. do end
do 
  puts 'Hi'
end
# example: arguments are wrapped between ||
[1, 2].map {|num| num * 2}

# Proc
#1. Proc.new followed by a code block
Proc.new { puts 'Hi' }
Proc.new do
  puts 'Hi'
end
#2. proc followed by a code block
proc { puts 'Hi' }
proc do
  puts 'Hi'
end
# example: arguments are wrapped between ||
times_2 = proc { |num| num * 2 }

# Lambda
# 1. lambda followed by a code block
lambda { puts 'Hi' }
lambda do
  puts 'Hi'
end
#2. -> (stabby lambda)
-> { puts 'Hi' }
-> do
  puts 'Hi'
end
#example: -> takes argument outside of {}
times_2 = -> (num) { num * 2 }

# Only -> takes argument outside of {}
->(arg) { puts arg }
# The rest takes argument in between ||
{ |arg| puts arg }
proc do |arg|
  puts arg
end
lambda { |arg| puts arg }

 

3. Calling a code block/proc/lambda

As we defined earlier, a proc/lambda is no more than an object containing a code block. The purpose of a proc/lambda is to save the code block and execute it later. So calling a proc/lambda is simply executing the code block it has.

How do you call a proc/lambda?

  • You call it.

How do you execute a code block?

  • You yield to it.1

Let’s first take a look at calling a proc/lambda.

times_2 = Proc.new { |num| num * 2 }
times_2.call(3) # returns 6

times_2 = proc { |num| num * 2 }
times_2.call(3) # returns 6

times_2 = lambda { |num| num * 2 }
times_2.call(3) # returns 6
  
times_2 = -> (num) { num * 2 }
times_2.call(3) # returns 6

Similar to the way a code block returns the result of its last line of code, a proc/lambda returns the result of the last of code in its code block.

 

Let’s take a look at yielding to a code block.

A code block can be attached to a method.

def burger
  puts 'top bun'
  yield if block_given?
  puts 'bottom bun'
end

burger do
  puts 'lettuce'
  puts 'tomato'
  puts 'chicken breast'
end

# the above code will print out
# top bun
# lettuce
# tomato
# chicken breast
# bottom bun

We can also pass arguments into a code block.

# Example: passing arguments
def ten_plus
  puts 'before yield'
  yield(10)
  puts 'after yield'
end

ten_plus do |num|
  puts(num + 100)
end

# the above code will print out
# before yield
# 110
# after yield

 

4. Passing and Returning Procs

Since procs are objects, it makes sense that we can pass procs as arguments to methods the same way we do with other objects (arrays, strings, ect).

# Passing a proc into a method
def give_me_a_proc(pr)
  puts 'Before call the proc'
  pr.call
  puts 'After calling the proc'
end

noisy_proc = proc { puts 'Noisy' }

give_me_a_proc(noisy_proc)

# The above code will print:
# Before call the proc
# Noisy
# After calling the proc

 

It shouldn’t be a surprise that we can also return a proc from a method.

# Returning a proc from a method
def fancy_proc_maker
  proc { puts 'Call me fancy' }
end

fancy_proc = fancy_proc_maker
fancy_proc.call
  
# The above code will print:
# Call me fancy

 


Woohoo! Now you have a sense of what code blocks, procs, lambdas, and closures are. I bet you can’t wait to share this TIL with your Ruby-buddies.

With the basics covered, next up is some fun time with Scopes, Universes, and Lunch Boxes!

Don't forget to subscribe! ?


[1]: You can also call a “block” by converting it to a proc first, which is what we will look at in part 3 of the series.

2 Comments Code Blocks, Procs, Lambdas, and Closures in Ruby

Leave A Comment

Your email address will not be published. Required fields are marked *