Understanding Ruby Closures

What is a closure?

Closures are functions that are bound to the environment that they were defined in. They retain access to variables that were in scope when they were created, but that might no longer be in scope when the closure is called. This example demonstrates access to a local variable that would otherwise be out of scope.

1 original = 1
2 def revive local_arg
3   Proc.new {local_arg}
4 end
5 
6 p = revive original
7 original = nil
8 p.call
9 => 1

Blocks

Blocks are the most commonly used form of closures in Ruby. You can find them all over the core Ruby libraries. See the Enumerable mixin for some great examples as well as my justification for why I like Ruby blocks.

Think of blocks as just a chunk of code that will be yielded to or called in the method you supply it to.

Blocks are defined after the last parameter to a method call between {} or do/end. Each call to yield in the method will invoke the block that was passed to that method. You can specify arguments to yield that will be passed to the block. If you do this, you must also specify the same parameters in the block definition. Each method can only receive a single block argument; although, it may yield to this single block multiple times. Calling yield without supplying a block will result in an error.

 1 def execAlgorithm count
 2   yield(yield count) if block_given?
 3 end
 4 
 5 execAlgorithm(2) { |x| x + 1 }
 6 => 4
 7 
 8 execAlgorithm 2 do |x|
 9   x + 1
10 end
11 => 4

In this example, execAlgorithm yields 2 to the block, which results in 2+1=3. It then yields this result again to the same block, which results in 3+1=4.

Procs

A Proc is a closure and an object that can be bound to a variable and reused. Procs are defined by calling Proc.new or proc followed by a block. It can also be created by calling the #to_proc method of a Method, Proc, or Symbol. A Proc can be invoked through its #call method (see below).

Lambdas

Lambdas are a more strict form of Proc. They are defined with lambda or ->(). The differences between a lambda and a proc are:

  • Lambdas are more strict with regard to argument checking.
  • Lambdas can return a value with the return keyword.
1 a = ->(x,y) {x+y}
2 a.call 2,3
3 => 5
4 a.class
5 => Proc

The & Operator

This operator works differently depending on what it is applied to. I've found that it has caused a high degree of confusion among new Ruby developers. It is relevant to the above sections in the following ways:

  • Leading an explicit block parameter with & (ie &block) will convert the block to a proc.
1 def a &block
2   block.call 2 # block is now a proc so it has a call method available
3 end
4 
5 a() {|x| puts x}
6 2
7 => nil
  • Leading a proc argument with & will convert the proc to a block.
1 say = Proc.new {puts 'what?'}
2 2.times &say # times accepts a block and yields to it
3 what
4 what
5 => 2
  • Leading anything else with & will call #to_proc on the object and then convert the proc to a block as above.
1 [1,2].map &:to_s
2 => ["1", "2"]

If you have further questions or comments about Ruby closures, blocks, procs, or lambdas, please leave a comment.

comments powered by Disqus