Pages

Wednesday, September 25, 2013

proc and lambda

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:
  1. p, l = proc{}, lambda{}
  2. p.class
  3. #=> Proc
  4. l.class
  5. #=> 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.
  1. p = proc{|a,b| p "The value of a is #{a.inspect}. The value of b is #{b.inspect}"}
  2. p.call 1,2
  3. # => "The value of a is 1. The value of b is 2"
  4. p.call 1
  5. # => "The value of a is 1. The value of b is nil"
  6. p.call 1,[2,3],Class.new
  7. # => "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?
  1. l = lambda{|a,b| p "The value of a is #{a.inspect}. The value of b is #{b.inspect}"}
  2. l.call 1,2
  3. # => "The value of a is 1. The value of b is 2"
  4. l.call 1
  5. # => ArgumentError: wrong number of arguments (1 for 2)
  6. # from (irb):74:in `block in irb_binding'
  7. # from (irb):75:in `call'
  8. # from (irb):75
  9. l.call [2,3],1,3
  10. # => ArgumentError: wrong number of arguments (3 for 2)
  11. # from (irb):74:in `block in irb_binding'
  12. # from (irb):75:in `call'
  13. # 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 lambdainstead 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
  1. p.call [1,2]
  2. # => "The value of a is 1. The value of b is 2"
  3. l.call [1,2]
  4. # => ArgumentError: wrong number of arguments (1 for 2)
  5. # from (irb):74:in `block in irb_binding'
  6. # from (irb):75:in `call'
  7. # from (irb):75
  8. l.call *[1,2]
  9. # => "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
  1. p.lambda?
  2. # => false
  3. l.lambda?
  4. # => 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
  1. l = lambda do
  2. "line 1"
  3. return "line 2"
  4. "line 3"
  5. end
  6. l.call
  7. # => "line 2"
  8.  
  9. p = proc do
  10. "line 1"
  11. return "line 2"
  12. "line 3"
  13. end
  14. p.call
  15. # => LocalJumpError: unexpected return
  16. # from (irb):170:in `block in irb_binding'
  17. # from (irb):173:in `call'
  18. # 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 procreturns explicitly, you jump immediately outside the method:
  1. def proc_foo
  2. p = Proc.new { return 'I will get returned by the method :o' }
  3. puts "You will see me"
  4. p.call
  5. puts "You will not see me because the method returned :("
  6. end
  7. proc_foo
  8. # You will see me
  9. # => "I will get returned by the method :o"
and that is not true when you use lambda
  1. def lambda_foo
  2. l = lambda { return puts 'I will not get returned by the method. I will get printed by the method!' }
  3. puts "You will see me"
  4. l.call
  5. puts "You will see me too because the method haven't returned :)"
  6. end
  7. lambda_foo
  8. # You will see me
  9. # I will not get returned by the method. I will get printed by the method!
  10. # You will see me too because the method haven't returned :)
  11. # => 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.
    1. Proc.new(&l).lambda?
    2. #=> true
    3. Proc.new(&p).lambda?
    4. #=> 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)
    1. l = ->(a,b=1){p "a equals #{a} and b equals #{b}"}
    2. l.call 1,2
    3. # => "a equals 1 and b equals 2"
    4. l.lambda?
    5. # => true
  3. There are two different ways of calling a Proc object.
    1. p = proc{|a,b| p "The value of a is #{a.inspect}. The value of b is #{b.inspect}"}
    2. p.call 1,2
    3. # => "The value of a is 1. The value of b is 2"
    4. p[1,2]
    5. # => "The value of a is 1. The value of b is 2"

No comments:

Post a Comment