Ruby blocks, procs & lambdas.
What are they?
How do they work?
How are they different from each other?
You will learn that & a lot more by reading this post!
Understanding Ruby Blocks
Ruby blocks are little anonymous functions that can be passed into methods.
Blocks are enclosed in a do / end
statement or between brackets {}
, and they can have multiple arguments.
The argument names are defined between two pipe |
characters.
If you have used each
before, then you have used blocks!
Here is an example:
# Form 1: recommended for single line blocks [1, 2, 3].each { |num| puts num } ^^^^^ ^^^^^^^^ block block arguments body
# Form 2: recommended for multi-line blocks [1, 2, 3].each do |num| puts num end
A Ruby block is useful because it allows you to save a bit of logic (code) & use it later.
This could be something like writing data to a file, comparing if one element is equal to another, or even printing an error message.
Ruby Yield Keyword
What does yield
mean in Ruby?
Yield is a Ruby keyword that calls a block when you use it.
It’s how methods USE blocks!
When you use the yield
keyword, the code inside the block will run & do its work.
Just like when you call a regular Ruby method.
Here’s an example:
def print_once yield end print_once { puts "Block is being run" }
This runs any block passed to print_once
, as a result, "Block is being run"
will be printed on the screen.
Did you know…
That yield
can be used multiple times?
Every time you call yield
, the block will run, so this is like calling the same method again.
Example:
def print_twice yield yield end print_twice { puts "Hello" } # "Hello" # "Hello"
And just like methods…
You can pass any number of arguments to yield
.
Example:
def one_two_three yield 1 yield 2 yield 3 end one_two_three { |number| puts number * 10 } # 10, 20, 30
These arguments then become the block’s arguments.
In this example number
.
Implicit vs Explicit Blocks
Blocks can be “explicit” or “implicit”.
Explicit means that you give it a name in your parameter list.
You can pass an explicit block to another method or save it into a variable to use later.
Here is an example:
def explicit_block(&block) block.call # same as yield end explicit_block { puts "Explicit block called" }
Notice the &block
parameter…
That’s how you define the block’s name!
How To Check If A Block Was Given
If you try to yield
without a block you will get a no block given (yield)
error.
You can check if a block has been passed in with the block_given?
method.
Example:
def do_something_with_block return "No block given" unless block_given? yield end
This prevents the error if someone calls your method without a block.
What is a Lambda?
A lambda is a way to define a block & its parameters with some special syntax.
You can save this lambda into a variable for later use.
The syntax for defining a Ruby lambda looks like this:
say_something = -> { puts "This is a lambda" }
You can also use the alternative syntax:
lambda
instead of->
.
Defining a lambda won’t run the code inside it, just like defining a method won’t run the method, you need to use the call
method for that.
Example:
say_something = -> { puts "This is a lambda" } say_something.call # "This is a lambda"
There are other ways to call
a lambda
, it’s good to know they exist, however, I recommend sticking with call
for clarity.
Here’s the list:
my_lambda = -> { puts "Lambda called" } my_lambda.call my_lambda.() my_lambda[] my_lambda.===
Lambdas can also take arguments, here is an example:
times_two = ->(x) { x * 2 } times_two.call(10) # 20
If you pass the wrong number of arguments to a lambda
, it will raise an exception, just like a regular method.
Lambdas vs Procs
Procs are a very similar concept…
One of the differences is how you create them.
Example:
my_proc = Proc.new { |x| puts x }
There is no dedicated Lambda
class. A lambda
is just a special Proc
object. If you take a look at the instance methods from Proc
, you will notice there is a lambda?
method.
Now:
A proc behaves differently than a lambda, specially when it comes to arguments:
t = Proc.new { |x,y| puts "I don't care about arguments!" } t.call # "I don't care about arguments!"
Another difference between procs
& lambdas
is how they react to a return
statement.
A lambda
will return
normally, like a regular method.
But a proc
will try to return
from the current context.
Here’s what I mean:
If you run the following code, you will notice how the proc
raises a LocalJumpError
exception.
The reason is that you can’t return
from the top-level context.
Try this:
# Should work my_lambda = -> { return 1 } puts "Lambda result: #{my_lambda.call}" # Should raise exception my_proc = Proc.new { return 1 } puts "Proc result: #{my_proc.call}"
If the proc
was inside a method, then calling return
would be equivalent to returning from that method.
This is demonstrated in the following example.
def call_proc puts "Before proc" my_proc = Proc.new { return 2 } my_proc.call puts "After proc" end p call_proc # Prints "Before proc" but not "After proc"
Here is a summary of how procs
and lambdas
are different:
- Lambdas are defined with
-> {}
and procs withProc.new {}
. - 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
ArgumentError
exception.
Taking a look at this list, we can see that lambdas
are a lot closer to a regular method than procs
are.
Closures
Ruby procs & lambdas also have another special attribute. When you create a Ruby proc, it captures the current execution scope with it.
This concept, which is sometimes called closure, means that a proc
will carry with it values like local variables and methods from the context where it was defined.
They don’t carry the actual values, but a reference to them, so if the variables change after the proc is created, the proc will always have the latest version.
Let’s see an example:
def call_proc(my_proc) count = 500 my_proc.call end count = 1 my_proc = Proc.new { puts count } p call_proc(my_proc) # What does this print?
In this example we have a local count
variable, which is set to 1
.
We also have a proc named my_proc
, and a call_proc
method which runs (via the call
method) any proc or lambda that is passed in as an argument.
What do you think this program will print?
It would seem like 500
is the most logical conclusion, but because of the ‘closure’ effect this will print 1
.
This happens because the proc is using the value of count
from the place where the proc was defined, and that’s outside of the method definition.
The Binding Class
Where do Ruby procs & lambdas store this scope information?
Let me tell you about the Binding
class…
When you create a Binding
object via the binding
method, you are creating an ‘anchor’ to this point in the code.
Every variable, method & class defined at this point will be available later via this object, even if you are in a completely different scope.
Example:
def return_binding foo = 100 binding end # Foo is available thanks to the binding, # even though we are outside of the method # where it was defined. puts return_binding.class puts return_binding.eval('foo') # If you try to print foo directly you will get an error. # The reason is that foo was never defined outside of the method. puts foo
In other words, executing something under the context of a binding
object is the same as if that code was in the same place where that binding
was defined (remember the ‘anchor’ metaphor).
You don’t need to use binding
objects directly, but it’s still good to know this is a thing 🙂
Video Tutorial
Wrapping Up
In this post you learned how blocks work, the differences between Ruby procs & lambdas and you also learned about the “closure” effect that happens whenever you create a block.
One thing I didn’t cover is the curry method.
This method allows you to pass in some or all of the required arguments.
If you only pass in a partial number of arguments you will get a new proc with these arguments already ‘pre-loaded’, when all the arguments are supplied then the proc will be executed.
I hope you enjoyed this post!
Don’t forget to subscribe in the form below and share this with your friends 🙂
Well explained!
Thank you 🙂
A wonderful article, Jesus, so nicely written!
Thanks for reading 🙂
Good article, Jesus. I think that it is important to understand differences between lambda and proc. This article describe differences politely by coding example, so readers easily understand.
And block is a feature to incarnate Closure.
Thanks for your comment 🙂
There is also another interesting behavior of procs:
def proc_from
Proc.new
end
proc = proc_from { “hello” }
proc.call #=> “hello”
If Proc.new is called within a method with an attached block, that block is converted to the Proc object.
(example from http://ruby-doc.org/core-2.2.0/Proc.html)
Very helpful, thank you. However there is one think confusing, I checked Kernel module and I see lambda method which is creating Proc object but you mentioned, lambda is a special object of Proc class. Your thoughts?
The
lambda
method onKernel
is the same as using the->
syntax for defining a lambda.Thank you, I had the same assumption 🙂
Good article: clear and gives a good overview!
Some minor nitpicking:
– Blocks are anonymous functions.
– Usually the alternative delimiters to do…end are called “curly braces” or “curly brackets” – “brackets” are (…) if I’m not mistaken.
– There is another, older and thus potentially more compatible syntax to create lambdas: lambda {|arg| …} and lambda do |arg| … end