Why Ruby? Part Two - Blocks and Closures

ruby method, ruby language, lambda, proc, ruby tutorial, block

Let’s go on with diving into the beauties of ruby - this time check out how elegant blocks and closures and their first-class support in ruby can make our codes.

First of all, blocks/closures in ruby are first-class citizens, not some mystic language extension that needs special conditions to use - in fact blocks are all around ruby.

The first cornerstone of blocks/closures is the concept of Proc objects. As the ruby documentation describes:

Proc objects are blocks of code that have been bound to a set of local variables. Once bound, the code may be called in different contexts and still access those variables

Perhaps it’s better to call Proc objects function objects - anonymous function objects. Actually Procs serve as functions in ruby - I guess all of you noticed by now that ruby uses methods everywhere, def’s create methods, modules and classes hold methods of course, et cetera - but no real, standalone functions. Procs fill this gap, some other terminology for them is functor. So, let’s see how a Proc looks like:

def get_a_multiplier(factor)
    return Proc.new {|n| n*factor }
end

times3 = get_a_multiplier(3)
times5 = get_a_multiplier(5)

times3.call(12)               #=> 36
times5.call(5)                #=> 25
times3.call(times5.call(4))   #=> 60

So, what does a Proc object have to do with closures for example? Well, a Proc object can act as a closure! What exactly is a closure anyway? It’s a (anonymous) function that has references to free variables in its context. Huh? Let’s see a ruby example of closure (we’ll get to understand yield later, for now it’s just a way of calling the caller block):

def my_if cond
  yield if cond
end

a = 5
my_if(a < 10) {
  puts "a is less than 10"
  a += 1
}

# prints "a is less than 10"
# returns 6

Put another way, a closure is a block of code that can be passed around as a value and can be executed and can refer to variables in that context it was defined in. This fits the description of a Proc object.

Also, Procs are first-class objects in ruby. They can be created runtime, stored, retrieved, passed as arguments and be return values.

Let’s see our next construct, the good old lambda. A lambda in ruby is almost exactly a Proc, with two slight but important differences:

  • the lambda kernel method results in an object that checks the number of its arguments, while a plain Proc.new does not:
my_lambda = lambda {|x, y| puts x + y}
my_proc = Proc.new {|x, y| puts x + y}

# works as expected, prints 6
my_proc.call(1, 5, 11)

# an ArgumentError exception is thrown because of the extra argument
my_lambda.call(1, 5, 11)
  • return calls in a lambda is handled differently than in a Proc. A return from Proc.new returns from the enclosing method, while return from lambda acts more conventionally, returning to its caller, see:
def return_from_a_proc
    ret = Proc.new { return "Here I go from a proc" }
    ret.call
    "This is not reached"
end

# prints "Here I go from a proc"
puts return_from_a_proc

def return_from_a_lambda
    ret = lambda { return "Here I go from a lambda" }
    ret.call
    "This is printed"
end

# prints "This is printed"
puts return_from_a_lambda

So, we reached the concept of block. Blocks are very much like Proc objects - unbound Procs. While a Proc.new explicitely creates something that is like a value, a block is the body itself. But why the fuss? What gives sense to the blocks’ existence if they are merely like the body of a Proc? Actually blocks are just convenient “shortcuts” for a common use-case: passing a single block of code to a method that can interact with it. Instead of explicitely creating an object with Proc.new and then passing it, we have a shortcut either with the do…end or the {…} syntax. An iteration is a fine example:

myarray = [1,2,3,4,5]

myarray.each { |item|
  puts item
}

# produces:
# 1
# 2
# 3
# 4
# 5

This is a big win that makes (or can make) ruby code clean and nice. It eliminated temporary variables, itchy workarounds and helps producing self-enclosed codes that speak for themselves.

How does a method interact with its block?

First, one direction of communication is the method passing values to the block - this is the iteration variable seen in the previous example. From the implementation of the method, the yield keyword is used to achieve this. Think of yield as if it was replaced by the block with the variables bound to the values of yield’s (optional) parameters. The other way around, yield has a return value, which is the return value (last evaluated expression) of the block. See it in a very simple example:

def method
  val = yield 0
  puts "value of yield0 : #{val}"
  val = yield 1
  puts "value of yield1 : #{val}"
end

method {|i| "return_val#{i}"}

# produces:
# value of yield0 : return_val0
# value of yield1 : return_val1

A good example of using closures/blocks is the Array class or rather the module Enumerable :

myarray = [1,2,3,4,5]

myarray.find_all { |array_item| array_item > 3 }    # [4, 5]
myarray.map { |array_item| array_item * 2 }         # [2, 4, 6, 8, 10]

### and something more hardcore:

class Array
  def sum
    inject { |sum,x| sum+x }
  end

  def mean
    sum / size
  end
end

myarray.sum  # 15
myarray.mean # 3

I hope this short article whets the appetite of many readers to try ruby :)

Comments

Copyright © 2013 Csaba Okrona . Powered by Octopress