ruby_cool_kid.rb — Blocks, Proc and Lambdas the close siblings. Part 1
When writing Ruby code you might have wondered: what is a block, the difference between a Proc
and a lambda
, and what makes them so special.
Well, you came to the right place 😉
What is a block in Ruby?
A block is just an anonymous (they do not have their own name) function or piece of code that can accept arguments or return value. Blocks can be invoked within a function or be passed to a method call.
One of the most common usages of blocks when writing Ruby code is inside calls to methods of the Enumerable class ( each
, each_with_index
, select
, ..).
Let’s suppose we have an array containing our grocery shopping list for the week. However, the strings are not capitalised or some of the have mixed cases.
Based on the previous scenario, let’s see how can we use blocks inside the Enumerable
class to format our list.
It is usual that we see blocks formatted as:
do….end statements
In this case, I have used map!
to iterate through the array and store the result back in the original array. It iterates the items in the array converting them to downcase, then capitalising the result.
curly braces {} statements
The same process as above can be done in curly braces as a one line as:
This patterns might look like magic but, in the end, what we are doing is passing the block as an argument to a method.
In the case above, the passed block to the map!
method is modifying the items int the array, that belongs to the Enumerable
class. But, how is this possible?
The Yield Keyword
If you have been reading or writing Ruby code for some time, you might have come across the yield
keyword. If you haven’t, this is it 😉.
What is yield
? It is simply a statement that indicates, usually a method, to call a block inside its execution.
The block is given to the method as an argument, then called with the yield
keyword, as it happened with map!
. Let’s see an example:
On this example, we want to show what we will do in our day.
Our grocery shopping list can vary from day to day, thus we want to pass it to our method rather than hardcoding it.
However, the list is not formatted and the item’s names look weird, we need to format them. Also, we need a string rather than the whole array. For that, we perform the operations in a block.
{ grocery_shopping.map { |item| item.downcase.capitalize }.join(', ') }
This block can be straight up used without needing a variable to store it, so we pass it to our todays_shopping_list
function.
Then, by using the yield
statement, it is included into the printed string as yield
calls (executes) the block.
Blocks can also be passed parameters, these parameters can be accessed through the pipe operator |p1, p2|
.
Let’s picture a simple greeting method using: a block, the yield
keyword and a parameter.
In this case, we are using name
as an argument for the block, passing it to the yield
statement.
We can check if a block has been passed to our method by calling block_given?
, which will return true or false.
Moving on to more complex usages. As we all know, in Ruby everything is an object, right? Well, what about blocks? Yes!….Nope, that is not an object.
But that is a topic for another article as this was dense enough. Stick around for the next part 😉.
For now, here are key points from this article:
- Blocks are anonymous chunks of code that are passed to methods
- Blocks are not objects, as they are not instantiated from any class nor have inheritance chains or instance variables. They are just bound to the scope where they were defined, and cannot be stored in variables
- Blocks are usually passed formatted as
do...end statements
andcurly braces {} statements
- We use
yield
to call blocks inside methods as they are anonymous - One can check if a block has been given by calling
block_given?
- Blocks can be passed parameters, that can later be accessed using the pipe operator
As usual, thanks a lot for taking time to read me and have a good day.
See you around 💃
Pablo.