Closures in Ruby – Part2

Blocks, Procs,  Lambdas and Methods come under the category of closures in Ruby. The concept of Closures is explained in detail at Part1 of this blog. In this we blog we are going to discuss in detail about Blocks, Procs, Lambdas and Methods.

Blocks & Procs

Blocks are chunks of code that can be passed to a method. This can be achieved in many ways. Try following the below sample codes to learn more.

class Maths

def initialize(val)

@val = yield(val)

p @val

end

def print

yield(@val)

end

end

num = Maths.new(10){|val| val = val * 10}

num.print{ |val| val = val * 20

p val}

Output:

100

2000

In all the method definitions inside the class ‘Maths’, the key word ‘yield’ means that it would accept any block of  code passed to it. When the object ‘num’ is created from the class Maths the ‘initialize’ method is invoked which accepts a parameter named ‘val’. This variable ‘val’ is then yielded to the block of code passed during the creation of the object “{|val| val = val * 10}”. The ‘val = 10’ is thus multiplied by 10 and the result ‘100’ in then stored in @val.

The print method once again yields this ‘@val’ to another block of code passed during the ‘print’ method call and gets multiplied by 20 and the result 2000 is printed as the result. Thus we can understand how blocks can be manipulated to perform different actions depending upon the requirement.

Now let us imaging that we want to perform the same actions on 5 more objects say num1, num2, num3, num4, num5. We should explicitly pass the blocks of code for each object as below:

num1 = Maths.new(10){|val| val = val * 10}

num1.print{ |val| val = val * 20

p val}

num2 = Maths.new(10){|val| val = val * 10}

num2.print{ |val| val = val * 20

p val}

etc..

Does this not make the code redundant?  The cost of coding will also be high. It would be great if we can store this block of code in a variable and pass the variable to each object to achieve the same results, right? This is where Procs come into picture. Look at the below code:

class Maths

def initialize(val,code)

@val = code.call(val)

end

def print(code)

code.call(@val)

end

end

mul = Proc.new {

|val| val = val * 10

p val

}

mul1 = Proc.new {

|val| val = val * 20

p val

}

num = Maths.new(10,mul)

num.print(mul1)

Output:

100

2000

The variable ‘mul’ and ‘mul1’ stores the block of code and is passed to the methods. This achieves the same result as the block. Also, if we want to perform the same operation of multiple objects we just have to use the proc appropriately. Example ‘num1 = Maths.new(20,mul) , num2 = Maths.new(29,mul)’ etc.

Interestingly, blocks are Proc objects! Look at the code below:

def methodnew(&block)

block.class

end

p methodnew{p “I am a block”}

Output:

Proc

Having learnt about blocks, it would be much easier to understand what Proc is and when should we use them. Procs are useful if we wish to execute a block of code multiple times.

Lambdas:

Let us look at the below example code to understand Lambdas:

class Maths

def initialize(val,code)

@val = code.call(val)

end

def print(code)

code.call(@val)

end

end

mul = lambda {

|val| val = val * 10

p val }

mul1 = lambda {

|val| val = val * 20

p val}

num = Maths.new(10,mul)

num.print(mul1)

Output:

100

2000

Lambdas are similar to Procs but the difference is, lambdas checks the number of arguements that is passed to it but a Proc does not check the number of arguements passed to it. For example

class Maths

def initialize(val,code)

@val = code.call(val)

end

end

mul = Proc.new {

|val,val1| val = val * 10

p val

p val1

}

mul1 = lambda {

|val, val1| val = val * 10

p val

p val1

}

num = Maths.new(10,mul)

num = Maths.new(10,mul1)

Output:

The output after calling the Proc:

100

nil

The output after calling Lambda:

example3.rb:29:in `block in <main>’: wrong number of arguments (1 for 2) (ArgumentError)

from example3.rb:11:in `call’

from example3.rb:11:in `initialize’

from example3.rb:35:in `new’

from example3.rb:35:in `<main>’

There is also one more difference between Procs and Lambdas.  Let us look at the below code:

class Maths

def initialize(val)

@val =val

end

def print(code)

code.call(@val)

return “return from method”

end

end

mul = Proc.new {

|val| val = val * 10

p val

return “return from Proc”

}

mul1 = lambda {

|val| val = val * 10

p val

return “return from Lambda”

}

num = Maths.new(10)

p num.print(mul1)

Output :

100

“return from method”

We see that Lambda continues code execution even after encountering return inside lambda.  Whereas Proc does not permit return commands when passed to a method and gives error as below:

p num.print(mul)

Output:

100

example3.rb:30:in `block in <main>’: unexpected return (LocalJumpError)

from example3.rb:17:in `call’

from example3.rb:17:in `print’

from example3.rb:45:in `<main>’

If you call a Proc directly inside a method without passing it as a parameter like the code below:

def method1

Proc.new{return “proc”}.call

return “from method1”

end

def method2

lambda{return “lambda”}.call

return “from method2”

end

p method1  #Output : proc

p method2 # Output: from method2

Lambda thus continues its execution even after encountering a return whereas Proc stop its execution after encountering first return.  When we expect out code to strictly check the number of arguments and continue program execution till the end we should use Lambdas else we should use Procs.

Methods:

‘Methods’ are another way of passing a block of code to a method and is similar to Lambdas. For example:

class Maths

def initialize(val,code)

@val = code.call(val)

end

def print(code)

code.call(@val)

end

end

def mul(val)

val = val * 10

p val

end

def mul1(val)

val = val * 20

p val

end

num = Maths.new(10,method(:mul))

num.print(method(:mul1))

Output:

100

2000

Thus we come to the conclusion of Closures in Ruby. I hope this blog was useful to you.

References:

1. Closure (computer science). (n.d.). , from http://www.princeton.edu/~achaney/tmve/wiki100k/docs/Closure_(computer_science).html

2. Alan Skorkin : “http://www.skorks.com/2010/05/closures-a-simple-explanation-using-ruby/

3. Robert Sonsinski: “http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/

Advertisements

Leave a comment

Filed under Uncategorized

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s