cult3

What are Lambdas in Ruby?

May 13, 2015

Table of contents:

  1. What are lambdas?
  2. What is the difference between a lambda and a Proc?
  3. Conclusion

Over the last couple of weeks we’ve looked at working with blocks and Procs.

A block is a chunk of code that can be passed to a method. This makes it really easy to write flexible methods that can be used in a number of different ways. If you are already familiar with other programming languages, this concept is probably already familiar to you.

Last week we looked at Procs. A Proc is basically just a block, but it is saved to a variable so you can use it like an object. This means you can make reusable procedures out of Procs that can be passed to methods. You can also use multiple Procs in a method call, whereas you can only use a single block.

Ruby also has a third similar concept to blocks and Procs known as lambdas. In today’s tutorial we’ll be looking at lambdas and how they differ from Procs.

What are lambdas?

If you already have a background in programming, you might have already come across the word lambda. A lambda is also commonly referred to as an anonymous function.

To create a lambda in Ruby, you can use the following syntax:

lambda = lambda {}

Alternatively you can use this syntax:

lambda = -> {  }

However, if you create a new lambda in IRB using either of these two syntaxes, you might have noticed something a bit weird:

# => #<Proc:0x007f933a429fd0@(irb):4 (lambda)>

If you call the class method you will see that a lambda is actually an instance of the Proc class:

lamba.class
# => Proc

What is the difference between a lambda and a Proc?

So if a lambda is also an instance of the Proc class, what is the difference between a lambda and a regular Proc and why is there a distinction?

Well, a lambda will behave like a method, whereas a Proc will behave like a block. Let’s dig into this so we understand what’s going on under the hood.

How arguments are handled

The first difference between Procs and lambdas is how arguments are handled.

For example, we might have the following lambda and Proc that do exactly the same thing, in this case, accept a name and puts a string to the screen:

lambda = ->(name) { puts "Hello #{name}" }

proc = Proc.new { |name| puts "Hello #{name}" }

We can call each of these by using the call method and passing a name as the argument:

lambda.call('Philip')
# => Hello Philip

proc.call('Philip')
# => Hello Philip

All good so far, both the lambda and the Proc behave in exactly the same way.

However, what happens if me don’t pass an argument?

lambda.call
# => ArgumentError: wrong number of arguments (0 for 1)

proc.call
# => Hello

When a lambda expects an argument, you need to pass those arguments or an Exception will be thrown. However, in the case of the Proc, if the argument is not passed it automatically defaults to nil.

This is because a lambda will act like a method and expect you to pass each of the defined arguments, whereas a Proc will act like a block and will not require strict argument checking.

The use of the return statement

A second difference between a lambda and a Proc is how the return statement is handled.

To illustrate this, lets take a look at a code example:

def lambda_method
  -> { return 'I was called from inside the lambda' }.call
  return 'I was called from after the lambda'
end

Here we have a method that contains a lambda and an return statement. When the lambda is called it will return a string of text to the method.

When we call this method and puts the return value to the screen, what would you expect to see?

puts lambda_method
# => "I was called from after the lambda"

So when the method is called, the lambda is called from inside the method, then the return statement returns the string of text after the lambda.

However, imagine we also had a proc version of this method:

def proc_method
  Proc.new { return 'I was called from inside the proc' }.call
  return 'I was called from after the proc'
end

This is basically the same method but instead of using a lambda we are using a Proc.

Now if we run this method, what would you expect to see?

puts proc_method
# => "I was called from inside the proc"

When a lambda encounters a return statement it will return execution to the enclosing method.

However, when a Proc encounters a return statement it will jump out of itself, as well as the enclosing method.

To further illustrate this behaviour, take a look at this example:

-> { return }
# => #<Proc:0x007f9d1182d100@(irb):2 (lambda)>

When you create a lambda in irb and use a return statement everything is fine.

However if you try to do the same thing with a Proc, you will get an Exception:

Proc.new { return }.call
# LocalJumpError: unexpected return

This is basically the same as what we saw whilst wrapping the lambda and the Proc in a method, however in this case, the Proc has nothing to jump back to.

Conclusion

Blocks, Procs and Lambdas are all pretty similar. Each has their own characters, place and purpose within the Ruby language.

It is important to understand the characteristics of things like blocks, Procs and lambdas because it will make it a lot easier to understand other people’s code.

When you learn a new idea it often feels tempting to jump right in and start using it all the time. This usually leads you to using the new technique in the wrong situations.

Don’t worry about using new ideas straightaway. Instead, start reading other people’s code to see how they have implemented the same idea.

Once you can understand and recognise how and why another developer has written a certain piece of code, you will be much better equipped to make your own design decisions.

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.