proc and lambda in Ruby

proc and lambda in Ruby – Introduction

Today we are going to talk a little bit about the infamous procs and lambdas in Ruby and the differences between them. Well, you may already know that a proc and a lambda are objects of the same class:

p, l = proc{}, lambda{}
p.class
#=> Proc
l.class
#=> Proc

So is proc an alias for lambda ? The answer is no. The Proc object returned by a call to proc{} has differences from the Proc object that the lambda{} call returns (This is only true for Ruby 1.9, as of Ruby 1.8 the proc and lambda was just aliases. In Ruby 1.8 the Proc object returned by a call to Proc.new was different from lambda).

proc and lambda in Ruby – Differences – # of arguments

The main difference between them is that in the first case the Proc object does not care at all for the number of arguments you pass to it e.g.

p = proc{|a,b| p "The value of a is #{a.inspect}. The value of b is #{b.inspect}"}
p.call 1,2
# => "The value of a is 1. The value of b is 2"
p.call 1
# => "The value of a is 1. The value of b is nil"
p.call 1,[2,3],Class.new
# => "The value of a is 1. The value of b is [2,3]"

So you see that it is gonna take only the first two arguments (for our example) and will discard the remaining, but if you give it less than two e.g. one it will assume that the other(s) are nil. So what about lambda?

l = lambda{|a,b| p "The value of a is #{a.inspect}. The value of b is #{b.inspect}"}
l.call 1,2
# => "The value of a is 1. The value of b is 2"
l.call 1
# => ArgumentError: wrong number of arguments (1 for 2)
#      from (irb):74:in `block in irb_binding'
#      from (irb):75:in `call'
#      from (irb):75
l.call [2,3],1,3
# => ArgumentError: wrong number of arguments (3 for 2)
#      from (irb):74:in `block in irb_binding'
#      from (irb):75:in `call'
#      from (irb):75

As you can see lambda is very strict about it’s number of arguments. I can’t give it neither more or less arguments than those specified the time it was defined. This is why many believe that using lambda instead of proc is preferable because you don’t want to silently ignore the argument mismatching. But if you do want that kind of behavior you should go ahead and use the proc instead.

proc and lambda in Ruby – Differences – argument expansion

If you do that, you get a bonus feature: proc can expand arguments when you give it an array without the use of the splat * operator

p.call [1,2]
# => "The value of a is 1. The value of b is 2"
l.call [1,2]
# => ArgumentError: wrong number of arguments (1 for 2)
#      from (irb):74:in `block in irb_binding'
#      from (irb):75:in `call'
#      from (irb):75
l.call *[1,2]
# => "The value of a is 1. The value of b is 2"

which is a nice feature of proc. In fact you can check whether a Proc object has the features of a proc (argument flexibility and argument expansion) by examining if it is a lambda. This is done by the call of lambda? method on a Proc object. When it returns true it means no argument expansion and flexibility

p.lambda?
# => false
l.lambda?
# => true

proc and lambda in Ruby – Differences – return from inside

Now the differences between proc and lambda do not stop here. You can’t explicitly return inside a proc but inside a lambda you can

l = lambda do
    "line 1"
    return "line 2"
    "line 3"
end
l.call
# => "line 2"

p = proc do
    "line 1"
    return "line 2"
    "line 3"
end
p.call
# => LocalJumpError: unexpected return
#        from (irb):170:in `block in irb_binding'
#        from (irb):173:in `call'
#        from (irb):173

What proc does and lambda don’t is that it exposes the return on the outside scope thus getting the LocalJumpError. In fact this is the reason why if you call a proc inside a method and the proc returns explicitly, you jump immediately outside the method:

def proc_foo
     p = Proc.new { return 'I will get returned by the method :o' }
     puts "You will see me"
     p.call
     puts "You will not see me because the method returned :("
end
proc_foo
# You will see me
# => "I will get returned by the method :o" 

and that is not true when you use lambda

def lambda_foo
     l = lambda { return puts 'I will not get returned by the method. I will get printed by the method!' }
     puts "You will see me"
     l.call
     puts "You will see me too because the method haven't returned :)"
end
lambda_foo
# You will see me
# I will not get returned by the method. I will get printed by the method!
# You will see me too because the method haven't returned :)
# => nil 

proc and lambda in Ruby – Notes


Notes:

  1. If you pass a Proc object as an argument to a Proc object the properties remain untouched e.g.
    Proc.new(&l).lambda? 
    #=> true
    Proc.new(&p).lambda? 
    #=> false
    
  2. As of Ruby 1.9 there is a nicer syntax of defining a lambda (in fact this syntax is not only syntactic sugar, that way you can predefine argument values)
    l = ->(a,b=1){p "a equals #{a} and b equals #{b}"}
    l.call 1,2
    # => "a equals 1 and b equals 2"
    l.lambda?
    # => true
    
  3. There are two different ways of calling a Proc object.

    p = proc{|a,b| p "The value of a is #{a.inspect}. The value of b is #{b.inspect}"}
    p.call 1,2
    # => "The value of a is 1. The value of b is 2"
    p[1,2]
    # => "The value of a is 1. The value of b is 2"
    

7 Comments proc and lambda in Ruby

    1. Gerry

      It is confusing at the beginning, but if you get a grasp of how things work you’ll never get confused again :)! Glad I helped anyways.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *


*