Closures in Ruby:
Blocks, Procs and Lambdas

01 SEPTEMBER 2013 | @ahsan_s | Software | Software | 2400 | 10
 A 12th century church door in Bergen, Norway. © 2004 Farl

Recently I've been spending some time trying to better understand Ruby Closures in general, and Blocks, Procs and Lambdas in particular. Fortunately, there are a large number of articles available on the internet on the subject. Some of them are really good and I'll be listing those that I particularly benefitted from at the end of this article.

So why another article on the topic? You can think of this like a "note to self". I extracted what I needed to remember as a reference for myself. If anyone else benefits from it too, that'll be a bonus!

The article is somewhat code-heavy and therefore assumes general familiarity with Ruby syntax. If you're looking for a more comprehensive treatment of the topic under discussion, I encourage you to read some of the articles listed at the end of this post.




Closure

A closure is a block of code that meets the following criteria[1]:

  • It can be passed around as a value
  • It can be executed on demand by anyone who has that value
  • It can refer to variables from the context in which it was created (even if those variables are no longer in scope)

Closure is a feature of functional programming languages like Scala and Haskell. Fortunately Ruby, even though not a functional programming language, supports closure and the benefits that come with it.

What are those benefits? The benefit that matters the most in practice is the fact that code written in languages that support closure tend to be clear and concise. Languages that don't support it tend to be verbose. You can see an example in this article[2] by Martin Fowler.

Closures can also be helpful in the following scenarios[3]:

  • Refactoring
  • Customization
  • Iterating across collections
  • Managing resources
  • Enforcing policy

You can see some good examples of the above in the article[3].

In Ruby, blocks, procs and lambdas all qualify as closures, which we will discuss next. You can think of these as three pillars of closure in Ruby.




Blocks

Blocks in Ruby are really syntactical construct of putting do/end or {} after a method invocation. When you write code with this syntactical construction, Ruby arranges for the code in the block to be passed to method being called[4].







Block: A Simple Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Method that expects a block
# as an implicit argument
def say_something
  yield # yield execution to the block
end

# Call the method with a block
say_something {puts 'Hello!'}

# Which is the same as the following
say_something do
  puts 'Hello!'
end

# Output:
# Hello!

A few things to notice:

  • The block is not indicated in the say_something method definition (but it can be, as we'll see later)
  • The presence of the yield statement in the body of the say_something method is the only indication that the method expects a block
  • The yield keyword returns the block's return value
  • In the above example the block is like an anonymous function

If a method that expects a block is called without it, like:
  say_something
it'll throw a LocalJumpError exception. Depending on the situation throwing an exception may be the right thing to do. But there's a way to avoid it where appropriate, as shown below.


A Safer yield:
1
2
3
4
5
6
# Method that practices safe yield
# will not throw an exception
# even if called without a block
def say_something
  yield if block_given?
end


A Block with an Argument:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Method that expects a block that takes an argument
def say_something_to_friend
  # Argument 'my friend!' passed to the block
  yield('my friend!')
end

# Call the method with a block that takes an argument
say_something_to_friend {|arg| puts "Hello, #{arg}"}

# Which is again the same as the following
say_something_to_friend do |arg|
  puts "Hello, #{arg}"
end

# Output:
# Hello, my friend!


A (Somewhat) Useful Block Example:

Here we'll create a method that measures the time it takes to execute a block[5]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Method that measures the execution time
# of any block passed to it
def measure_time
  start = Time.now
  yield
  puts "Completion time: #{Time.now - start} sec."
end

# Call measure_time with a block
# whose execution time is to be measured
measure_time do
  # Do something useful
  sleep 3
end

# Or simply
measure_time {sleep 3}

# Output:
# Completion time: 3.000095 sec.


Iterators & Blocks Example:

Probably a more common use of blocks is in conjunction with Ruby iterator methods (each, each_with_index, map/collect, select/find_all, reject, sort, sort_by, find, inject, detect, etc.).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Iterator Example-1: each
# 'each' yields to the block iteratively,
# passing each element of the array to it

[1,2,3,4,5].each {|elem| print "#{elem*2} "}

# Output:
# 2 4 6 8 10


# Iterator Example-2: each_with_index
# Key, Value and Index of the hash elements
# are passed to the block

h = {:a => 26, :b => 27, :c => 28}
h.each_with_index do |(key, val), index|
  puts "#{index}.  #{key} => #{val}"
end

# Output:
# 0.  a => 26
# 1.  b => 27
# 2.  c => 28

We'll discuss more on Ruby iteration methods in the next post on September 15, 2013.




Procs

Procs, short for procedures, are very similar to blocks, but with a few significant differences:

Blocks vs. Procs
  • Unlike blocks, procs can be assigned to local variables
  • Unlike blocks, more than one procs can be passed to a method
  • Unlike blocks, Procs are executed using the call keyword.


Proc: A Simple Example:
1
2
3
4
5
6
# Create a Proc and assign to a local variable 'greeting'
greeting = Proc.new { puts "Hello World!" }
greeting.call

# Output:
# Hello World!


A Proc with an Argument:
1
2
3
4
5
6
7
8
9
10
11
# Method that expects a proc that takes an argument
def say_something_to_friend(proc)
  # Argument 'my friend!' passed to the proc
  proc.call('my friend!')
end

# Call the method with a proc that takes an argument
say_something_to_friend Proc.new {|arg| puts "Hello, #{arg}"}

# Output:
# Hello, my friend!


Use of '&' to Convert Between Blocks and Procs:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Example-1: Convert Proc to Block

proc_2_block = Proc.new {|elem| print "#{elem*2} "}

# Array's 'each' method expects a block,
# but we're calling it with a proc
# The '&' converts the proc to a block 
[1,2,3,4,5].each(&proc_2_block)

# Output:
# 2 4 6 8 10



# Example-2: Convert Block to Proc

# The method is called with a block later
# The & converts the block to a proc
def say_something_to_friend(&block_2_proc)
  # 'call' is used to execute a proc
  block_2_proc.call('my friend!')
end

# Call the method with a block
say_something_to_friend {|arg| puts "Hello, #{arg}"}

# Output:
# Hello, my friend!


Another interesting way to write Example-2: Convert Block to Proc is as follows[6]:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Example-2 again: Convert Block to Proc

# The method is called with a block later
# Notice how Proc.new converts the block to a proc
def say_something_to_friend
  # The passed block is implicitly assumed below
  block_2_proc = Proc.new
  block_2_proc.call('my friend!')
end

# Call the method with a block
say_something_to_friend {|arg| puts "Hello, #{arg}"}

# Output:
# Hello, my friend!



A Few Shortcuts

Following common syntax:

1
2
greeting = Proc.new {|arg| puts "Hello, #{arg}"}
greeting.call('world!')

can also be written a number of ways using shortcuts[6]:

Commonly used Shortcut Example
Proc.new proc
greeting = proc {|arg| puts "Hello, #{arg}"}
.call() .()
greeting.('world!')
.call() []
greeting['world!']
.call() ===
greeting === 'world!'





Lambdas

Lambdas look and feel very similar to Procs. Let's first look at the syntax:

Lambda: A Simple Example:
1
2
3
4
5
6
# A lambda that takes an argument
lam = lambda { |x| print "#{x*2}" }
[1,2,3].each(&lam)

# Output:
# 2 4 6

Notice that the array's each method expects a block, but because lambdas are like Procs, an ampersand (&) is used to convert the lambda to a block when the method is called. The above example can also be written with an alternate lambda syntax with a funny name:

Stabby Lambda:
1
2
3
4
5
6
# Stabby lambda syntax of the previous example
lam = ->(x) { print "#{x*2}" }
[1,2,3].each(&lam)

# Output:
# 2 4 6

In other words, the following two lines are equivalent:

1
2
lam = lambda { |x| print "#{x*2}" }  # regular lambda
lam =  ->(x) { print "#{x*2}" }      # stabby lambda


Procs vs. Lambdas

Despite the fact that lambdas are used almost identically as Procs in the above examples, there are a couple of significant differences between the two:

  • Lambdas check number of arguments passed in, Procs don't (Procs ignore any missing arguments, assigning nil to them). Example[5] follows:

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    # Example: Proc takes 2 arguments
    p = Proc.new { |x, y| puts "x: #{x.class}, y: #{y.class}" }
    # But called with one
    p.call(7)
    
    # Output:
    # x: Fixnum, y: NilClass
    
    
    # Example: Lambda takes 2 arguments
    l = lambda { |x, y| puts "x: #{x.class}, y: #{y.class}" }
    # But called with one
    l.call(7)
    
    # Output:
    # ArgumentError: wrong number of arguments (1 for 2)}

    (Proc and Lambda both support a method named arity that returns the number of arguments expected[7]).

  • Lambdas and Procs treat the return keyword differently. Lambdas return to the calling method. Procs return immediately without going back to the caller. (You can think of Procs as drop-in code snippets, where a return statement will leave the method calling the Proc, whereas lambdas behave like method calls). Example follows:

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    
    # Define a method that can be called
    # with a Proc or a Lambda
    def some_method(arg)
      arg.call
      puts 'Hello from some_method'
    end
    
    # Create a Lambda
    l = lambda { puts 'Hello from Lambda'; return }
    # Create a Proc
    p = Proc.new { puts 'Hello from Proc'; return }
    
    
    # Call the method with the Lambda
    some_method(l)
    
    # Output:
    # Hello from Lambda
    # Hello from some_method
    
    
    # Call the method with the Proc
    some_method(p)
    
    # Output:
    # Hello from Proc


Even though not often mentioned, another difference between Proc and Lambda has to do with how the break keyword is handled. Having a break keyword within a lambda will cause us to break out of the lambda itself, but within a proc we get an error if there is no iterator around the proc and we break out of the iterator if we do have one[8].




Closure Scope Retention

and a problem associated with it

We haven't really seen an example of Ruby closure's ability to refer to variables that have gone out of scope (I wanted to wait until we discussed Blocks, Procs and Lambdas). One problem that is associated with this is that it keeps Ruby garbage collector from reclaiming variables (declared within the same scope as the closure) that take up a lot of memory, even if they are not used by the closure. It is therefore a good idea to assign nil to such variables as soon as they are no longer needed. Example follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# A method that creates and returns a Proc.
# It also uses a lot of memory before doing so
def create_proc
  # Create an array with 10 million numbers
  large_array = Array.new(10000000, 0)
  # Use the array to do something useful
  # ...
  # Reset when done
  large_array = nil

  # Create and return Proc
  message = 'Hello World!'
  Proc.new { puts message }
end

# Call the method to create a proc
proc = create_proc

# Variable 'message' is now out of scope
# but proc retains a reference to it,
# as is evident from
# the output of the following

proc.call

# Output:
# Hello World!

If we didn't set large_array to nil, Ruby would still hang on to the memory allocated by the array (by reference), as long as as proc is in scope.




Closures as Function Generators

An interesting aspect of closure is that it can also be used to dynamically generate function behavior. Following example[6] demonstrates this:

1
2
3
4
5
6
7
8
9
10
11
12
13
def multiple_generator(m)
  lambda { |n| n*m }
end

doubler = multiple_generator(2)
tripler = multiple_generator(3)

puts doubler.call(5)  # => 10
puts tripler.call(10) # => 30

# Or, using the shortcut [] for .call()
puts doubler[5]  # => 10
puts tripler[10] # => 30




Closures used with case statements

Another interesting and clever use of closures is with Ruby case statements. However, I'm leaving it for the reader to explore[6],[9]. Personally, I prefer not to use clever techniques in code, only because they may make the code hard to understand for future maintainers. But if you are so inclined, you should consider providing adequate references with the code. The technique, however, is definitely worth exploring.






Wrapping Up

As I mentioned at the outset of the article, this is a note to self. Personally, I'll be using this as a reference to remind me of the usage of and the differences between Blocks, Procs and Lambdas. If you find this useful too, that'll make the effort even more worthwhile.






References:

  1. Closures in Ruby by Paul Cantrell
  2. Lambda by Martin Fowler, 2004
  3. Crossing borders: Closures by Bruce Tate, 2007
  4. Jim Weirich on the differences between procs and lambdas in Ruby by Mislav Marohnić
  5. Know your closures: blocks, procs, and lambdas, Interconnected ideas by @biesnecker, 2013
  6. An Introduction to Procs, Lambdas and Closures in Ruby. Peter Cooper, Cooper Press, YouTube 2011
  7. Help and documentation for the Ruby programming language.
  8. Ruby Procs And Lambdas (And The Difference Between Them) by Alan Skorkin, 2010
  9. Lambdas/Procs in Case Expressions by Bozhidar Batsov, 2013


More articles: