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:
- If you pass a
Proc
object as an argument to aProc
object the properties remain untouched e.g.- Proc.new(&l).lambda?
- #=> true
- Proc.new(&p).lambda?
- #=> false
- 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
- 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"
No comments:
Post a Comment