Lambdas—Same, But Different

Procs have a lot of similarities with lambdas. In fact, you might be surprised to learn that a lambda is a Proc:

 >>​ lambda {}.class
 =>​ Proc

A Proc, however, is not a lambda:

 >>​ proc {}.class
 =>​ Proc

Fortunately, Ruby has a helpful predicate method that lets you disambiguate procs and lambdas via the Proc#lambda? method:

 >>​ lambda {}.lambda?
 =>​ ​true
 
 >>​ proc {}.lambda?
 =>​ ​false

You might be wondering why there isn’t a corresponding proc? method. Some thinking leads you to realize that this method would be pretty useless. Let’s assume the method existed:

 # NOTE: This method doesn't exist!
 >>​ lambda {}.proc?
 =>​ ​true
 
 # NOTE: This method doesn't exist!
 >>​ proc {}.proc?
 =>​ ​true

This hypothetical example shows that a method like proc? would not in any way help us differentiate between a lambda and a Proc, since both are Procs.

Invoking lambdas is identical to invoking Procs, with the exception of the lambda keyword:

 lambda { |x, y| x + y }.call(x, y)
 lambda { |x, y| x + y }[x, y]
 lambda { |x, y| x + y }.(x, y)
 lambda { |x, y| x + y } === [x, y]

If you find typing lambda too verbose for your taste, Ruby offers an alternative, affectionately known as the stabby lambda syntax:

 ->(x, y){ x + y }.call(x, y)
 ->(x, y){ x + y }[x, y]
 ->(x, y}{ x + y }.(x, y)
 ->(x, y}{ x + y } === [x, y]

If you tilt your head a certain way and cross your eyes a little, you might see that -> looks like the Greek letter lambda, λ.

So now that you’ve seen the similarities between lambdas and procs, what’s the difference between these two? And since they’re so similar, when should you use one or the other?

The Difference Between a Lambda and a Proc

A lambda and a proc have two important differences: arity and return semantics.

Arity refers to the number of arguments a function takes. While this definition usually applies to functions or methods, it is also applicable to lambdas and procs. Fire up irb and let’s do some exploring. Create a lambda and a proc:

 >>​ l = lambda { |x, y| puts ​"x: ​​#{​x​}​​, y: ​​#{​y​}​​"​ }
 
 >>​ p = proc { |x, y| puts ​"x: ​​#{​x​}​​, y: ​​#{​y​}​​"​ }

Then invoke them:

 >>​ l.call(​"Ohai"​, ​"Gentle Reader"​)
 
 >>​ p.call(​"Ohai"​, ​"Gentle Reader"​)

You’ll see the following result from each:

 x: ​Ohai, ​y: ​Gentle Reader

Now here comes the interesting bit. What happens if we supply one less argument? Here’s Proc’s response:

 >>​ p.call(​"Ohai"​)
 #​=>​ ​x: ​Ohai, ​y:

The Proc seems perfectly fine having less than expected arguments. What about lambda?

 >>​ l.call(​"Ohai"​)
 =>​ ArgumentError: wrong number of arguments (1 ​for​ 2)

Turns out, lambdas get upset if you give them less arguments than expected. Now, let’s try the same exercise but with one extra argument. Again, here’s Proc:

 >>​ p.call(​"Ohai"​, ​"Gentle"​, ​"Reader"​)
 #​=>​ ​x: ​Ohai, ​y: ​Gentle Reader

Here’s the lambda:

 >>​ l.call(​"Ohai"​, ​"Gentle"​, ​"Reader"​)
 #​=>​ ArgumentError: wrong number of arguments (3 ​for​ 2)

So, the moral of the story is that lambdas, unlike Procs, expect the exact number of arguments to be passed in. For Procs, unassigned arguments are given nil. Extra arguments are silently ignored.

Now, let’s look at the other difference: return semantics. A Proc always returns from the context it was created. Let’s unpack this a little and see why this is important. Create a new file called some_class.rb with the following contents:

 class​ SomeClass
 
 def​ method_that_calls_proc_or_lambda(procy)
  puts ​"calling ​​#{​proc_or_lambda(procy)​}​​ now!"
  procy.call
  puts ​"​​#{​proc_or_lambda(procy)​}​​ gets called!"
 end
 
 def​ proc_or_lambda(proc_like_thing)
  proc_like_thing.lambda? ? ​"Lambda"​ : ​"Proc"
 end
 
 end

SomeClass has two methods:

method_that_calls_proc_or_lambda takes a Proc or lambda, prints out a message, then invokes the Proc or lambda.

Depending on how the Proc or lambda returns (which you’ll see later), the final puts statement will execute.

proc_or_lambda is a tiny helper function that tells us if proc_like_thing is a lambda or Proc. Let’s begin the experiment with the lambda:

 >>​ c = SomeClass.new
 
 >>​ c.method_that_calls_proc_or_lambda lambda { ​return​ }

With a lambda, the second puts statement is executed:

 calling Lambda now!
 Lambda gets called!

Next, do the same thing, but use a proc instead:

 >> c = SomeClass.new
 
 >> c.method_that_calls_proc_or_lambda proc { ​return​ }

Observe:

 'block in <main>'​: unexpected ​return​ (LocalJumpError)

What just happened? Not only did the second puts statement not execute, but we landed ourselves onto a LocalJumpError. In order to understand why, let’s go back to the line that started off this section:

A Proc always returns from the context it was created.

So, what is the context from which the Proc was created?

 c.method_that_calls_proc_or_lambda proc { ​return​ }

It should be clear by now that the Proc was created in the main context. Therefore, proc { return } means returning from the main context. That is impossible, because the main context is the top-most level. Look at the error again:

 'block in <main>': unexpected return (LocalJumpError)

Now you can understand what the error means. The <main> bit refers to the main context. The unexpected return is due to the Proc returning from the main context, an impossible feat.

Joe asks:
Joe asks:
Should I Use lambdas or Procs?

For the most part, you should be fine using lambdas. That’s because the return semantics of lambdas resemble the intuitive behavior of methods.

Sometimes though, you might want to use Procs. One reason might be you need multiple arities to be supported. You’ll learn more in the Symbol#to_proc section where you’ll reimplement a very nifty Ruby trick made possible by Procs.

Now let’s review the Ruby method Symbol#to_proc and see how it’s implemented.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.210.205