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

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

This is part 2 of a three-part series.

Part 1 covers the following four fundamentals:

  • Definitions of Code Block, Proc, Lambda, and Closure
  • Constructions
  • Calling a Code Block/Proc/Lambda
  • Passing and Returning Procs

Part 2, this post, covers

  • Scopes, Universes, and Lunch Boxes [FUN STUFF]
  • Differences between a Proc and a Lambda

Part 3 covers:

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

 

The most important takeaways 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 some extents, code blocks, procs, and lambdas, can be seen as closures.

Let’s jump right into the FUN!


Scopes, Universes, and Lunch Boxes

I like to think of a scope as a 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 happens 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 outside of the method. Although they both called applethey 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 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.

Differences between a proc and a lambda

Let’s revisit the definitions:

  • a proc  is an object that contains a code block
  • 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)
Proc.new { puts 'I am a proc' }
proc.new { puts 'I am a proc' }
  
lambda { puts 'I am a lambda' }
-> { puts 'I am a lambda' }

 

2. The 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 the 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.

Leave a Comment