223

And when would you use one rather than the other?

Tom Lehman
  • 85,973
  • 71
  • 200
  • 272

8 Answers8

303

One difference is in the way they handle arguments. Creating a proc using proc {} and Proc.new {} are equivalent. However, using lambda {} gives you a proc that checks the number of arguments passed to it. From ri Kernel#lambda:

Equivalent to Proc.new, except the resulting Proc objects check the number of parameters passed when called.

An example:

p = Proc.new {|a, b| puts a**2+b**2 } # => #<Proc:0x3c7d28@(irb):1>
p.call 1, 2 # => 5
p.call 1 # => NoMethodError: undefined method `**' for nil:NilClass
p.call 1, 2, 3 # => 5
l = lambda {|a, b| puts a**2+b**2 } # => #<Proc:0x15016c@(irb):5 (lambda)>
l.call 1, 2 # => 5
l.call 1 # => ArgumentError: wrong number of arguments (1 for 2)
l.call 1, 2, 3 # => ArgumentError: wrong number of arguments (3 for 2)

In addition, as Ken points out, using return inside a lambda returns the value of that lambda, but using return in a proc returns from the enclosing block.

lambda { return :foo }.call # => :foo
return # => LocalJumpError: unexpected return
Proc.new { return :foo }.call # => LocalJumpError: unexpected return

So for most quick uses they're the same, but if you want automatic strict argument checking (which can also sometimes help with debugging), or if you need to use the return statement to return the value of the proc, use lambda.

Phrogz
  • 296,393
  • 112
  • 651
  • 745
jtbandes
  • 115,675
  • 35
  • 233
  • 266
  • 14
    Would it accurate to say that lambdas are very much like methods (check arguments and return will return from them) while procs are very much like blocks (arguments are not checked and a return will return from the containing method or lambda)? – pedz May 04 '14 at 17:28
  • 10
    I've been to God knows how many websites and articles by now and nobody seems to talk about the utility of Procs vs. methods vs. lambdas. Every explanation just provides a hair-splitting detail of how return values, etc., are different, but none on why it matters. For now I have to conclude that this is a design mess in Ruby. – ankush981 Apr 06 '20 at 14:41
  • 1
    @ankush981 I agree that the args strictness could have been uniform (e.g always strict). But the return differences seem to have their place, I can imagine they have their use cases. Why do you determine it's a "design mess"? It's definitely not documented well though. – Joel Blum Mar 20 '22 at 09:38
102

The real difference between procs and lambdas has everything to do with control flow keywords. I am talking about return, raise, break, redo, retry etc. – those control words. Let's say you have a return statement in a proc. When you call your proc, it will not only dump you out of it, but will also return from the enclosing method e.g.:

def my_method
  puts "before proc"
  my_proc = Proc.new do
    puts "inside proc"
    return
  end
  my_proc.call
  puts "after proc"
end

my_method

shoaib@shoaib-ubuntu-vm:~/tmp$ ruby a.rb
before proc
inside proc

The final puts in the method, was never executed, since when we called our proc, the return within it dumped us out of the method. If, however, we convert our proc to a lambda, we get the following:

def my_method
  puts "before proc"
  my_proc = lambda do
    puts "inside proc"
    return
  end
  my_proc.call
  puts "after proc"
end

my_method
shoaib@shoaib-ubuntu-vm:~/tmp$ ruby a.rb
before proc
inside proc
after proc

The return within the lambda only dumps us out of the lambda itself and the enclosing method continues executing. The way control flow keywords are treated within procs and lambdas is the main difference between them

Andrey Deineko
  • 51,333
  • 10
  • 112
  • 145
Shoaib
  • 1,114
  • 1
  • 7
  • 17
27

There are only two main differences.

  • First, a lambda checks the number of arguments passed to it, while a proc does not. This means that a lambda will throw an error if you pass it the wrong number of arguments, whereas a proc will ignore unexpected arguments and assign nil to any that are missing.
  • Second, when a lambda returns, it passes control back to the calling method; when a proc returns, it does so immediately, without going back to the calling method.

To see how this works, take a look at the code below. Our first method calls a proc; the second calls a lambda.

def batman_ironman_proc
  victor = Proc.new { return "Batman will win!" }
  victor.call
  "Iron Man will win!"
end

puts batman_ironman_proc # prints "Batman will win!"

def batman_ironman_lambda
  victor = lambda { return "Batman will win!" }
  victor.call
  "Iron Man will win!"
end

puts batman_ironman_lambda # prints "Iron Man will win!"

See how the proc says "Batman will win!", this is because it returns immediately, without going back to the batman_ironman_proc method.

Our lambda, however, goes back into the method after being called, so the method returns the last code it evaluates: "Iron Man will win!"

Rajkaran Mishra
  • 4,532
  • 2
  • 36
  • 61
23

# Proc Examples

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

proc = Proc.new { puts "Hello World" }
proc.call

# Lambda Examples

lam = lambda { |x| puts x*2 }
[1,2,3].each(&lam)

lam = lambda { puts "Hello World" }
lam.call           

Differences between Procs and Lambdas

Before I get into the differences between procs and lambdas, it is important to mention that they are both Proc objects.

proc = Proc.new { puts "Hello world" }
lam = lambda { puts "Hello World" }

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

However, lambdas are a different ‘flavor’ of procs. This slight difference is shown when returning the objects.

proc   # returns '#<Proc:0x007f96b1032d30@(irb):75>'
lam    # returns '<Proc:0x007f96b1b41938@(irb):76 (lambda)>'

1. Lambdas check the number of arguments, while procs do not

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

In contrast, 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(2)                   # prints out 2
proc.call                      # returns nil
proc.call(1,2,3)               # prints out 1 and forgets about the extra arguments

2. Lambdas and procs treat the ‘return’ keyword differently

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

def lambda_test
  lam = lambda { return }
  lam.call
  puts "Hello world"
end

lambda_test                 # calling lambda_test prints 'Hello World'

‘return’ inside of a proc triggers the code outside of the method where the proc is being executed

def proc_test
  proc = Proc.new { return }
  proc.call
  puts "Hello world"
end

proc_test                 # calling proc_test prints nothing

And to answer your other query, which one to use and when ? I'll follow @jtbandes as he has mentioned

So for most quick uses they're the same, but if you want automatic strict argument checking (which can also sometimes help with debugging), or if you need to use the return statement to return the value of the proc, use lambda.

Originally posted here

Arvind singh
  • 1,312
  • 15
  • 15
4

Generally speaking, lambdas are more intuitive than procs because they’re more similar to methods. They’re pretty strict about arity, and they simply exit when you call return . For this reason, many Rubyists use lambdas as a first choice, unless they need the specific features of procs.

Procs: Objects of class Proc . Like blocks, they are evaluated in the scope where they’re defined. Lambdas: Also objects of class Proc but subtly different from regular procs. They’re closures like blocks and procs, and as such they’re evaluated in the scope where they’re defined.

Creating Proc

a = Proc.new { |x| x 2 }

Creating lambda

b = lambda { |x| x 2 }

spirito_libero
  • 1,206
  • 2
  • 13
  • 21
2

Here is another way to understand this.

A block is a chunk of code attached to the invocation to a call of a method on an object. In the below example, self is an instance of an anonymous class inheriting from ActionView::Base in the Rails framework (which itself includes many helper modules). card is a method we call on self. We pass in an argument to the method and then we always attach the block to the end of the method invocation:

self.card :contacts do |c|
  // a chunk of valid ruby code    
end

Ok, so we are passing a chunk of code to a method. But how do we make use of this block? One option is to convert the chunk of code into an object. Ruby offers three ways to convert a chunk of code into an object

# lambda
> l = lambda { |a| a + 1 }
> l.call(1)
=> 2 

# Proc.new
> l2= Proc.new { |a| a + 1 }
> l2.call(1)
=> 2 

# & as the last method argument with a local variable name
def add(&block)
end

In the method above, the & converts the block passed to the method into an object and stores that object in the local variable block. In fact, we can show that it has the same behavior as lambda and Proc.new:

def add(&block)
  block
end

l3 = add { |a| a + 1 }
l3.call(1)
=> 2

This is IMPORTANT. When you pass a block to a method and convert it using &, the object it creates uses Proc.new to do the conversion.

Note that I avoided the use of "proc" as an option. That's because it Ruby 1.8, it is the same as lambda and in Ruby 1.9, it is the same as Proc.new and in all Ruby versions it should be avoided.

So then you ask what is the difference between lambda and Proc.new?

First, in terms of parameter passing, lambda behaves like a method call. It will raise an exception if you pass the wrong number of arguments. In contrast, Proc.new behaves like parallel assignment. All unused arguments get converted into nil:

> l = lambda {|a,b| puts "#{a} + #{b}" }
 => #<Proc:0x007fbffcb47e40@(irb):19 (lambda)> 
> l.call(1)
ArgumentError: wrong number of arguments (1 for 2)

> l2 = Proc.new {|a,b| puts "#{a} + #{b}" }
=> #<Proc:0x007fbffcb261a0@(irb):21> 
> l2.call(1)
1 + 

Second, lambda and Proc.new handle the return keyword differently. When you do a return inside of Proc.new, it actually returns from the enclosing method, that is, the surrounding context. When you return from a lambda block, it just returns from the block, not the enclosing method. Basically, it exits from the call to the block and continues execution with the rest of the enclosing method.

> def add(a,b)
  l = Proc.new { return a + b}
  l.call
  puts "now exiting method"
end
> add(1,1)
=> 2  # NOTICE it never prints the message "now exiting method"

> def add(a,b)
  l = lambda { return a + b }
  l.call
  puts "now exiting method"
end
> add(1,1)
=> now exiting method  # NOTICE this time it prints the message "now exiting method"

So why this behavioral difference? The reason is because with Proc.new, we can use iterators inside the context of enclosing methods and draw logical conclusions. Look at this example:

> def print(max)
  [1,2,3,4,5].each do |val|
    puts val
    return if val > max
  end
end
> print(3)
1
2
3
4

We expect that when we invoke return inside the iterator, it will return from the enclosing method. Remember the blocks passed to iterators get converted to objects using Proc.new and that is why when we use return, it will exit the enclosing method.

You can think of lambdas as anonymous methods, they isolate individual blocks of code into an object that can be treated like a method. Ultimately, think of a lambda as behaving as an anomyous method and Proc.new behaving as inline code.

Daniel Viglione
  • 8,014
  • 9
  • 67
  • 101
1

A helpful post on ruby guides: blocks, procs & lambdas

Procs return from the current method, while lambdas return from the lambda itself.

Procs don’t care about the correct number of arguments, while lambdas will raise an exception.

Sindhu Shree
  • 158
  • 2
  • 11
-3

the differences between proc and lambda is that proc is just a copy of code with arguments replaced in turn, while lambda is a function like in other languages. (behavior of return, arguments checks)

Nighon
  • 1,034
  • 11
  • 13