What's the difference between a proc and a lambda in Ruby?
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
.
Differences between Proc and Lambda
There are two main differences between lambdas and non-lambda Proc
s:
- Just like methods, lambdas return from themselves, whereas non-lambda
Proc
s return from the enclosing method, just like blocks. - Just like methods, lambdas have strict argument checking, whereas non-lambda
Proc
s have loose argument checking, just like blocks.
Or, in short: lambdas behave like methods, non-lambda Proc
s behave like blocks.
What you are seeing there is an instance of #2. Try it with a block and a method in addition to a non-lambda Proc
and a lambda, and you'll see. (Without this behavior, Hash#each
would be a real PITA to use, since it does yield an array with two-elements, but you pretty much always want to treat it as two arguments.)
Speed differences between proc, Proc.new, lambda, and stabby lambda
So it seems you have three questions. The middle one is unclear to me, so I will address the other two:
Why is normal method invocation so much faster?
This is the easier of the questions.
First realize that the times involved here are for function call overhead. I did my own timings based on your code (but with an identity function instead of multiplication), and non-direct invocations took 49% longer. With one multiplication, non-direct invocations took only 43% longer. In other words, one reason why you're seeing a large disparity is that your function itself is doing almost nothing. Even a single multiplication makes 6% of the difference "vanish". In a method of any reasonable complexity, the method call overhead is usually going to be a relatively small percentage of the overall time.
Next, remember that a proc/block/lambda is essentially a chunk of code that is being carried around (though a block literal cannot be saved into a variable). This implies one more level of indirection than a method call...meaning that at the very least the CPU is going to have to traverse a pointer to something.
Also, remember that Ruby supports closures, and I'm betting there is some overhead in deciding which environment the indirect code should run in.
On my machine, running a C program that invokes a function directly has 10% less overhead than one that uses a pointer to a function. An interpreted language like Ruby, where closures are also involved, is definitely going to use more.
My measurements (Ruby 2.2) indicate a direct method invocation takes about as long as 6 multiplications, and an indirect invocation takes about as long as 10.
So while the overhead is nearly twice as large, remember that the overhead in both cases is often relatively small.
Are there any other (performance based) reasons to chose between the different approaches?
I'd say given the above data the answer is usually no: you're much better off using the construct that gives you the most maintainable code.
There are definitely good reasons to choose one over the other. To be honest, I'm surprised about the difference I see between lambdas and blocks (on my machine, lambdas have 20% less overhead). Lambdas create anonymous methods that include parameter list checking, so if anything I would expect it to be slightly slower, but my measurements put it ahead.
That the direct invocation is faster simply isn't surprising at all.
The place where this kind of thing makes a difference is in very frequently called code where the overhead adds up to be noticeable in wall-clock kinds of ways. In this case, it can make sense to do all manner of ugly optimizations to try to squeeze a bit more speed, all the way up to inlining code (dodge the function-call overhead altogether).
For example, say your application uses a database framework. You profile it and find a frequently-called, small (e.g. less than 20 multiplications worth of work) function that copies the data from the database result into data structures. Such a function might comprise the lion's share of the result processing, and simply inlining that function might shave off significant amounts of CPU time at the expense of some code clarity.
Simply inlining your "square function" into a long numeric calculation with a billion steps could save you dramatic amounts of time because the operation itself takes a lot less time than even a direct method invocation.
In most cases, though, you're better off with clean, clear code.
When to use lambda, when to use Proc.new?
Another important but subtle difference between procs created with lambda
and procs created with Proc.new
is how they handle the return
statement:
- In a
lambda
-created proc, thereturn
statement returns only from the proc itself - In a
Proc.new
-created proc, thereturn
statement is a little more surprising: it returns control not just from the proc, but also from the method enclosing the proc!
Here's lambda
-created proc's return
in action. It behaves in a way that you probably expect:
def whowouldwin
mylambda = lambda {return "Freddy"}
mylambda.call
# mylambda gets called and returns "Freddy", and execution
# continues on the next line
return "Jason"
end
whowouldwin
#=> "Jason"
Now here's a Proc.new
-created proc's return
doing the same thing. You're about to see one of those cases where Ruby breaks the much-vaunted Principle of Least Surprise:
def whowouldwin2
myproc = Proc.new {return "Freddy"}
myproc.call
# myproc gets called and returns "Freddy",
# but also returns control from whowhouldwin2!
# The line below *never* gets executed.
return "Jason"
end
whowouldwin2
#=> "Freddy"
Thanks to this surprising behavior (as well as less typing), I tend to favor using lambda
over Proc.new
when making procs.
What is the difference between a method and a proc object?
In brief:
a Method
object is "bound" to an object so that self
points to that object when you call
the method, and a Proc
doesn't have that behavior; self
depends on the context in which the Proc
was created/called.
However:
You said in your question that "methods are not objects," but you have to be careful to distinguish between "method" and Method
.
A "method" is a defined set of expressions that is given a name and put into the method table of a particular class for easy lookup and execution later:
class Foo
def my_method
return 123
end
end
Foo.new.my_method
# => 123
A Method
object (or similarly an UnboundMethod
object) is an actual Ruby object created by calling method
/ instance_method
/ etc. and passing the name of a "method" as the argument:
my_Foo = Foo.new
my_Method = my_Foo.method(:my_method)
# => #<Method: Foo#my_method>
my_Method.call
# => 123
my_UnboundMethod = Foo.instance_method(:my_method)
# => #<UnboundMethod: Foo#my_method>
my_UnboundMethod.bind(my_Foo).call
# => 123
A Proc
object is a set of expressions that is not given a name, which you can store for later execution:
my_proc = Proc.new { 123 }
my_proc.call
# => 123
You may find it useful to read the RDoc documentation for UnboundMethod
, Method
, and Proc
. The RDoc pages list the different instance methods available to each type.
Differences between Named Scopes, Lambdas and Procs
1. Proc doesn't check the parameters passed, but lambda does
proc1 = Proc.new { |a, b| a + 5 }
proc1.call(2) # call with only one parameter
=> 7 # no error, unless the block uses the 2nd parameter
lambda1 = lambda { |a, b| a + 5 }
lambda1.call(2)
=> ArgumentError: wrong number of arguments (1 for 2)
Proc will throw error only if the block uses the second param.
proc2 = Proc.new { |a, b| a + b }
proc2.call(2) # call with only one parameter
=> TypeError: nil can't be coerced into Fixnum
2. Proc returns from the calling method, while lambda doesn't
def meth1
Proc.new { return 'return from proc' }.call
return 'return from method'
end
puts meth1
=> return from proc
def meth2
lambda { return 'return from lambda' }.call
return 'return from method'
end
puts meth2
=> return from method
If they are not called inside a method,
proc1 = Proc.new { return "Thank you" }
proc1.call
=> LocalJumpError: unexpected return
lambda1 = lambda { return "Thank you" }
lambda1.call
=> "Thank you"
3. Scopes/Named scopes are a feature of Rails
It is used to specify commonly used queries which can be referenced as method calls on the association objects or models
eg, in user.rb:
scope :developers, -> { where(:role => 'developer') }
You can use it as
@developers = User.developers
Scopes are chainable, so you can do queries like
@developers = User.developers.where(:age => 40)
The scope defined in the example, is exactly same as defining a class method, like below.
def self.developers
where(:role => 'developer')
end
Lambda vs Proc in terms of memory and efficiency
There are several differences between Lambdas and Procs.
Lambdas have what are known as "diminutive returns". What that means is that a Lambda will return flow to the function that called it, while a Proc will return out of the function that called it.
def proc_demo
Proc.new { return "return value from Proc" }.call
"return value from method"
end
def lambda_demo
lambda { return "return value from lambda" }.call
"return value from method"
end
proc_demo #=> "return value from Proc"
lambda_demo #=> "return value from method"Lambdas check the number of parameters passed into them, while Procs do not. For example:
lambda { |a, b| [a, b] }.call(:foo)
#=> #<ArgumentError: wrong number of arguments (1 for 2)>
Proc.new { |a, b| [a, b] }.call(:foo)
#=> [:foo, nil]
Related Topics
What's the Best Way to Talk to a Database While Using Sinatra
Is There a Literal Notation for an Array of Symbols
Access Instance Variable from Outside the Class
Convert an Array of Integers into an Array of Strings in Ruby
Nested Object Creation with JSON in Rails
How to Set Up Cascade Deleting in Rails
Skip Over Iteration in Enumerable#Collect
Difference Between Plugins and Ruby Gems
Getting Only New Mail from an Imap Server
Count Number of Days Between Two Dates
How to Return Something Early from a Block
How to Check If a Class Is Defined
How to Sort a String's Characters Alphabetically
How to Add a New Action to the Existing Controller
Undefined Instance Method "Respond_To" in Rails 5 API Controller
Rails - Multiple Top Level Domains and a Single Session/Cookie