• Home
  • About
    • Evolving perceptions photo

      Evolving perceptions

      A space where I pen down my views and experiences

    • Learn More
    • Twitter
    • LinkedIn
    • Github
  • Posts
    • All Posts
    • All Tags
  • Projects

Blocks, Procs and Lambdas in Ruby

18 Feb 2016

Reading time ~5 minutes

Blocks

Blocks are chunks of code that can be passed around. We can’t find blocks just hanging around without some method. It doesn’t mean anything on a standalone basis and can only appear in argument lists.

You declare a block using braces {} if it’s on one line or do … end if it’s on multiple lines. This is just a convention followed in the Ruby community.

['s','a','i'].each { |chr| print "#{chr}" }

Blocks with input

Blocks can take inputs and can also return expressions. Blocks lets use the implicit return (whatever’s on the last line) and not the return keyword, since that will return us from whatever method actually called the block.

Blocks are just arguments

Blocks are used as arguments to other methods. They are nothing special, just plain arguments. They are listed last and on their own because they tend to take up multiple lines. Don’t think of them as anything too special.

yield

yield basically says “run the block right here”. yield can pass parameters to your block as well.

each method

Lets write the Ruby code for the each method on Arrays to get an idea of what’s happening behind the scenes.

class Array 
  def each
    i = 0
    while i < self.size
        yield(self[i])  
        i+=1      
    end
    self
  end
end

['s','a','i'].each { |chr| print "#{chr}!" }

Check if block was passed?

To check if block was passed at all(to only yield in that case): yield if block_given?

Passing multiple blocks

Can we pass multiple blocks to a function? Can we save a block and use it again later? Thats where the incredible procs(Procedures) comes into picture. Actually, a block is a Proc. The block can be imagined as a subclass of a Proc.


Procs

A Proc can be saved to a variable and reused again unlike blocks.

p = Proc.new { |x| puts x }
[1,2,3].each(&p)              
# The '&' tells ruby to turn the proc into a block 

Call

We use call for Procs instead of yield. This is quiet intuitive as we know that a function can accept multiple procs unlike a single block so yield in this case doesn’t mention which Proc has to be called. call literally just runs the Proc that is called on. It can take arguments as well.

p.call(“sailesh”)


Differences between Blocks and Procs

Procs are objects, blocks are not. A proc is an instance of the Proc class. They can be assigned to variables. Procs can also return themselves.

p = Proc.new { puts "Welcome!" }

p.call  # prints 'Welcome'
p.class # returns 'Proc'
a = p   # a now equals p, a Proc instance
p       # returns a proc object id

In contrast, a block is just part of the syntax of a method call.

{ puts "Welcome!"}       # syntax error  
a = { puts "Welcome!"}   # syntax error
['s','a','i'].each { |chr| print "#{chr}!" } # no error

We can pass multiple procs to methods.

def funct(proc1, proc2)
  proc1.call
  proc2.call
end

p1 = Proc.new { puts "proc 1" }
p2 = Proc.new { puts "proc 2" }

funct(p1,p2)

Lambdas

A lambda acts more like a real method. A lambda gives us more flexibility with what it returns (like if you want to return multiple values at once) because we can safely use the explicit return statement inside of one. With lambdas, return will only return from the lambda itself and not the enclosing method, which is what happens if you use return inside a block or Proc. Lambdas are also much stricter than Procs about passing them the correct number of arguments.

lambda do |arg| 
	puts arg
	return arg          # you can do this in lambdas not Procs
end.call("sailesh")
#we are chaining the call method on the lambda

Differences between Procs and Lambdas

Its important to note that they are both Proc objects.

proc = Proc.new { puts "Welcome!" }
lam = lambda { puts "Welcome!" }

proc.class # returns 'Proc'
lam.class  # returns 'Proc'

Lambdas check the number of arguments, while procs do not

lam = lambda { |x| puts x }    # creates a lambda that takes 1 argument
lam.call(5)                    # prints out 5
lam.call                       # ArgumentError: wrong number of arguments (0 for 1)
lam.call(7,8,9)                # ArgumentError: wrong number of arguments (3 for 1)

Procs don’t care if they are passed the wrong number of arguments.

proc = Proc.new { |x| puts x } # creates a proc that takes 1 argument
proc.call(5)                   # prints out 5
proc.call                      # returns nil
proc.call(7,8,9)               # prints out 7 and forgets about the extra arguments

return inside of a lambda triggers the code right outside of the lambda code


Closures

The term closure is often mistakenly used to mean anonymous function. This is probably because many programmers learn about both concepts at the same time, in the form of small helper functions that are anonymous closures. An anonymous function is a function literal without a name, while a closure is an instance of a function, a value, whose non-local variables have been bound either to values or to storage locations.

Proc objects preserving local context

def counter
  n = 0
  return Proc.new { n+= 1 }
end

a = counter
a.call            # returns 1
a.call            # returns 2

b = counter
b.call            # returns 1

a.call            # returns 3

Methods

This is very different from an actual method. They are a convenient way to pass a normal method to another normal method by wrapping the symbol of its name in the word method()



rubyclosuresblocksprocslambdasMethods Like Tweet +1