About Proc, Lambda and Block

I keep on searching for this topic, so I guess it’s time I put it on my blog.

This is an example of a proc:

the_proc = proc { puts "I'm the proc" }

To execute the_proc, I need to explicityly to use:

the_proc.call # or
the_proc.() # introduced in Ruby 1.9 or

You can create a lambda using this syntax:

the_lambda = lambda { puts "I'm the lambda" }
the_lambda = lambda { |s| puts "I'm the lambda" }
the_lambda = -> { puts "I'm the lambda" } # introduced in Ruby 1.9
the_lambda = -> (x, y) { puts "I'm the lambda" }

Just like with proc you can execute it using one of these three methods:

the_lambda.call # or
the_lambda.() # or

Differences between lambda and proc:

the_proc = proc { puts "proc"; return }
the_lambda = -> { puts "lambda"; return }

def test(arg)
  puts "first"
  puts "second"


# first
# proc
# LocalJumpError: unexpected return


# first
# lambda
# second

So, what is block in Ruby? Using the above example of proc, a block is actually the piece of code that sits between those two curly braces, in this case that would be: puts "proc"; return.

The only way to get access to the block is through the proc or lambda as block is not an object.

A basic example of block usage would be:

def output
  puts "start"
  puts "end"

output { puts "amree" }

As you can see from the example, we actually don’t have direct access to the block. In order to get access to that block, we need to wrap it in proc and this how you do it:

def output(&block)
  # ...

You can use block.call or just use the yield keyword to execute the block. The biggest benefit of this approach is that we can control in which context it’s being executed. This is very useful in building DSL.

For e.g, let’s take a look at a simple DSL (that looks like Rails router):

class Router
  def initialize
    @routes = {}

  def match(route)
    @routes.update route

  def routes(&block) # block will be converted into a Proc
    # block.call
    # This will be called in the root context which will throw an error as
    # routes method is defined in Router, not outside.

    # instance_eval { @routes }
    # It is expecting a block

    # Will convert it to a block when it's being used on method call

    puts @routes

Router.new.routes do
  match "/users" => "users#index"
  # Ruby will automatically convert it to hash because it's the last parameter.
  # Basically, it's the same as:
  # match({ "/users" => "users#index" })

  match "/login" => "sessions#new"

Written by Amree Zaid

Powered by Jekyll. Get the source at Github. Design copied from Sam Vermette.