Skip to main content

Ruby on rails part 6 - Blocks , lambda, procs and closure

 Blocks , lambda, procs  and closure

Table of content 

1. Blocks

2. Lambda

3. Procs

4. Closure

Blocks 

Ruby blocks are little anonymous functions that can be passed into methods.

Blocks are enclosed in a do-end statement or between brackets {}
.

Blocks can have multiple arguments
.

The argument names are defined between two pipe | characters.

Blocks are typically used with ‘each’ method which iterates over a list.

Syntax of block using {}

['List of items'].each { | block arguments|  block body } 

Syntax of block using do-end

['List of items'].each do | block arguments | 

    # block body

end

Example of block declared as do-end with each method.

 
[1, 2, 3].each do |num|
puts num
end 
 

Output

 
$ ruby block_with_each.rb
1
2
3
 

Blocks can also be saved in variables or passed as argument to another function. 

yield is a Ruby keyword that is used to call a block.

When you use the yield keyword, the code inside the block will run.

Example of saving a block in a variable.

 
varName = [1, 2, 3].each do |num|
puts num
end
puts varName 
 

Output

 
$ ruby block_variable.rb
1
2
3
1
2
3
 

Example of passing a block to a method and using yield. No parameter is needed to hold the incoming block argument.

 
def blockMethod
# yield is a command to call the block
# 1/2 written next to it is passed as an argument to the block
# The argument is then stored in the num parameter inside the block
yield 1
yield 2 # yield can be used multiple times
end
blockMethod { |num| puts num }
 

Output

 
$ ruby passing_block.rb
1
2
 

Execution flow of the above scenario:

blockMethod function is called

An argument (block) is passed to the function

Inside the function, yield invokes the block code

Yield passes an argument to the block code

The block code stores the argument in the num variable

The block then prints the num

Implicit vs Explicit block

Blocks without name are invoked using yield are called implicit blocks
.

When passing a block as an argument to a method, and the method stores the block in a variable, such block is called an explicit block. 

Example of an implicit block. No parameter is needed to hold the incoming block argument.

 
def implicitBlockMethod
yield
end

def explicitBlockMethod(&blockName)
blockName.call
blockName.()
blockName[]
blockName.===
end
implicitBlockMethod { puts "Implicit Block" }
explicitBlockMethod { puts "Explicit Block" }
 

Output

 
$ ruby implicit_explicit_block.rb
Implicit Block
Explicit Block
Explicit Block
Explicit Block
Explicit Block
 

Methods in ruby can take a block as an argument and call inside a method. In order to define a block as a parameter ruby has syntax with ampersand operator (&).

If you try to yield a block without passing the block as an argument, you will get a no block given (yield) error.

 
def blockMethod
# When no block is passed while calling this method
yield
end

blockMethod 
 

Output

 
$ ruby no_block_error.rb
a.rb:4:in `blockMethod': no block given (yield) (LocalJumpError)
from no_block_error.rb:7:in `<main>'
 

You can check if a block has been passed in with the block_given? method.

This prevents the error if someone calls your method without a block.

 
def blockMethod
# When no block is passed while calling this method
return "No block given" unless block_given?
yield
end

print(blockMethod)
 

Output

 
$ ruby block_check.rb
No block given
 

 Block is passed 

 
def blockMethod
    # When a block is passed while calling this method
return "No block given" unless block_given?
yield
end

print(blockMethod { puts "Block given"})
 

Output

 
ruby block.rb
Block given
 

Lambda 


A lambda is a way to define a block and parameters with a special syntax. A lambda function is just an anonymous function. Syntax of defining a lambda function.

lambdaName = -> { # code statement } 

                    OR

lambdaName = -> do

        # code statemenet

end

There are 4 different ways of calling a lambda function. Syntax of calling a lambda function is as below :

lambdaName.call

lambdaName.()

lambdaName[]

lambdaName.===

Lambda function can also take arguments. The x below is an argument. We can pass multiple arguments.

lambdaName = -> (x) { x * 2 }

                    OR

lambdaName = -> (x) do 

        puts "This is a lambda function argument : #{x}"

 end

Calling the lambda function as :

                                        lamndaName.call(10)

Procs

There is no dedicated Lambda class. A lambda is just a special Proc object.

Proc has a lambda? method that returns true if the proc is also a lambda. Syntax for defining procs. 

procName = Proc.new { |args| puts args } 

OR

procName = Proc.new do |args| 

                puts args 

end

Syntax to call the above proc

procName.call 1

procName.(1)

procName[1]

procName.=== 1

Check if a proc is a lambda

 
lambdaName = -> { return 1 }
procName = Proc.new {}
puts "Before: #{procName.lambda?}"
procName = lambdaName
puts "After: #{procName.lambda?}" 
 

Output

 
$ ruby proc.rb
Before: false
After: true
 

Proc vs Lambda

Lambdas are defined with -> {} and procs with Proc.new {}

Procs and blocks return from the current caller method, while lambdas return only from the lambda itself.


Procs don’t care about the correct number of arguments, while lambdas will raise an exception if the arguments are not correct.

 
# Proc doesn't care about the arguments
# No exception is thrown for wrong number of arguments
procName = Proc.new { |args| puts "Hello #{args}" }
procName.call # This will not raise any exception 
 

Output

 
$ ruby proc_no_error.rb
Hello
 

While lambda expression raise an ArgumentError

 
# Lambda cares about the arguments
# Invalid no/type of arguments will raise exceptions
lambdaName = -> (args) { puts "Hello #{args}" }
lambdaName.call # This will raise an exception because no arg is passed 
 

Output

 
$ ruby lambda_error.rb
a.rb:3:in `block in <main>': wrong number of arguments (given 0, expected 1) (ArgumentError)
from lambda_error.rb:4:in `<main>'
 

If the proc was inside a method, then calling return would be equivalent to returning from that method.


# Returning from a proc is equivalent of returning from its parent method
def procMethod
puts "Before proc"
procName = Proc.new { return 2 }
procName.call
puts "After proc" # Will not be executed
end

puts procMethod
 

Output

 
$ ruby proc_return.rb
Before proc
2
$
 

ensure block will always execute when returned from proc.


# Even if you return from a begin-rescue block
# ensure block will always be executed before returning
def exceptionTest
begin
5/0
rescue
procName = Proc.new { return "Return block" }
procName.call
ensure
puts "Ensure block"
end
end

puts exceptionTest
puts "Outside all blocks"
 

Output

 
$ ruby ensure_block.rb
Ensure block
Return block
Outside all blocks
 

Returning from a proc is equivalent of returning from its parent method
, while returning from a lambda is not equivalent of returning from its parent method
.

 
def lambdaMethod
puts "Before lambda"
lambdaName = -> { return "Returned from lambda" }
lambdaName.call
puts "After lambda" # Will be executed
end

puts lambdaMethod
 

Output

 
$ ruby lambda_return.rb
Before lambda
After lambda
$
 

Closures

Ruby procs and lambdas also have another special attribute that helps it capture the current execution scope with it. 


If a variable is not found in the current context/scope, it will not search the higher/global scopes.

This concept is called closure. Closures allows proc/lambda to carry with it values like local variables and methods from the 
context where it was defined.

They don’t carry the actual values, but a reference to them, so if the variables change after 
the proc is created, the proc will always have the latest version.

 
# outside the method is the topmost or the global context
count = 1
 
# inside the method is method level context
def call_proc()
count = 500
# Defining a proc inside a method adds the local count
# to its variable collection. Global count is
# not added because it's not in the current scope
my_proc = Proc.new { puts count } # count referred here is local 500
return my_proc
end

# my_proc is defined in the method context
# Hence it will contain values that are available
# in the same context
procVar = call_proc()
# Even though the method scope has ended eith the end of the method definition.
# The below call to proc prints the value of
# count as 500, because it still holds the context of the
# method in which the proc was defined
puts procVar.call # Prints 500 (Local Value)
puts count         # Print 1 (Global value) 
 

Thank you folks, if you like my post do check my other posts on Django with Python 3 and Ruby on Rails  on SWE crunch