Scopes, Universes, & Lunch Boxes; Procs vs Lambdas in Ruby

Sihui Huang
Gusto Engineering
Published in
5 min readJun 4, 2017

In Part 1, we talk about:

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

The most important take aways from part 1 are the definitions:

  • a code block is a block of code
  • a proc is an object that contains a code block
  • a lambda is a special type of proc
  • 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 in this post). To different extends, code blocks, procs, and lambdas, can be seen as closures.

In this part, we will discuss more advanced and fun topics:

5. Scopes, Universes, and Lunch Boxes [FUN STUFF]

6. Differences between Procs and Lambdas

The series ends with the grand final covering:

7. Proc <> Code Block Conversion and Ampersand(&) [FUN STUFF]

Let’s jump right into the FUN!

5. Scopes, Universes, and Lunch Boxes

I like to think of a scope as an universe.

A code block/proc/lambda/closure

preserves the local variable bindings that are in effect when it is created [1].

In other words, a code block/proc/lambda/closure is IN the universe that is in effect when it is created.

Let’s see it in action:

As we can see, the apple in the top level is reachable by both the proc and the code block.

A code block/proc/lambda/closure is not only in the universe that is in effect when it is created, it also can carry that universe with it.

We will talk more about the universe-carrying part in a second. Before that, let’s take a look at what happen when you create a class or a method.

When a class or a method is created, a new scope is created. They CREATE their own brand new universe.

Let’s see that in action as well:

The first time when we called the print_apple method, we got a NameError. That was because the apple was not in the method’s scope: the method was in a different universe. In the method’s universe, there was no apple.

Later when we redefined the method and created an apple inside the method, we could see that the apple inside the method was not the same apple as the one out side of the method. Although they both called apple, they are literary two different apples in two different universes.

Now, let’s go back to the idea of a code block/proc/lambda/closure carries its universe with it. We can see it in effect when two different universes come together — that’s when the fun begins! 😏😏😏

I will let the code speaks for itself:

In the first two lines, a blue universe and a proc were defined. Inside the m method, a red universe was defined. When we called passed_in_proc.call, the code, { universe }, held by the proc was executed, and the blue universe was returned.

You can think of the way a code block/proc/lambda/closure carries its universe with it the same way as you carry your lunch box with you to work. You put your meal and fruits inside your lunch box and take it with you. When you open it again at work, the meal and fruits are still there! Your office kitchen might have the same type of fruits, but they are not the same as the ones you take from home.

6. Differences between a proc and a lambda

Let’s revisit the definitions: a proc is an object that contains a code block, and a lambda is a special type of proc.

This is a metal picture you can have: procs = regular procs + lambdas

Tony Tony Chopper is here b/c he is cute!

There are three differences between a regular proc and a lambda.

  1. The ways they are constructed are different:
  • a regular proc is constructed by either Proc.new or proc
  • a lambda is constructed by either lambda or -> (stabby lambda)

2. They ways they return are different:

  • a lambda return from the code block it contains
  • a proc return from the scope that calls it

The code speaks clear and loud:

An excellent example from The Well-Grounded Rubyist

3. A lambda needs to be called with the exact number of arguments, where a proc is more loose about the arguments it receives.

Woohoo, that’s all I got for today. We have learned about scopes, universes, lunch boxes, and the difference between procs and lambdas.

Next up is the grand finale of the series where we will take an in-depth look at the use of ampersand (&) to convert procs and code blocks. It will be the most challenging and most fun part of the series. It’s something you don’t want to miss. 😏😏😏

[1]: Definition from The Well-Grounded Rubyist.

--

--