All About Blocks, Procs, and Lambdas in Ruby
So, what are blocks, procs, and lambdas and why should you care about them? To put it simply, they one of the coolest aspects of the Ruby language and they are the sole reason why we have the Enumerable class and it's really nifty methods like map which takes a list and a method and returns a new list where each item has been ran through the supplied method. Very cool. But, were getting ahead of ourselves. Before we can understand how cool methods like map work we fist need to define what a block is.
Blocks
A block in Ruby is any code enclosed by {}
or the keywords do end
. All blocks in Ruby are actually closures also called lambdas or procedures. A closure is a stored function with it's own internal variable scope and storage. So, they are like little programs within programs that can be passed into functions or returned by functions. So the title of this post is 'All About Blocks, Procs, and Lambdas in Ruby', but it could actually be shortened to just 'All About Blocks in Ruby'. The other keywords were added to the title because these terms are often used interchangeably and because despite there almost identical functionality there are slight differences between these three constructs in Ruby which will be discussed later. Let's look at a simple example.
Basically, what we are doing here is sending an anonymous block, the stuff between the curly braces, to the function mantra
which then runs that code by using the keyword yield
. The anonymous block requires a single parameter which is supplied by the yield
method's argument of 5. Now this is a trivial example, but it forms the basis for something like this.
We just created our own mapping function! Blocks can be really handy in getting rid of a lot of repetitive coding. For example, if you need to do testing where a piece of code is ran and then it's output or errors are formatted and printed you can simply wrap that code into a block and then send it to a testing function. If you want to send a query to a database and do error checking just wrap your query function within a block and send it to a database connecting function. The possibilities are endless. Now that we have covered block functionality lets go over a few notes about block syntax and a few gotchas.
Block Syntax
If you want to send arguments along with an anonymous block you can do this two different ways. If you use bracket notation to define a block the arguments must be put inside parenthesis. If you use do end
notation no parenthesis are required.
A block can also be sent to a function and stored in a parameter by using an &
before the last parameter name within the function header. Please note that this creates a procedure object which is then stored within that variable name and therefore you will have to use .call
instead of yield
.
So far we have only been creating procedures by sending anonymous blocks into functions and then using the &
syntax to convert these blocks into procedures. To create a procedure object and store a block of code inside it use the lambda
, proc
, or Proc.new
keywords.
Finally, bloc_given?
can be used within methods or functions to check and see if a block was submitted or not. This can be usefully when you want a method to have a default behavior which can subsequently be overwritten by a submitted block. This behavior is commonly seen with the sort method which will sort an array or object in ascending order, but it's behavior can be changed by submitting an anonymous block.
Block Gotchas
Always remember that variables defined in the main script are actually properties of an object from the Object class which all other classes are subclasses of. This means that every block can access and change any variable defined within the main script! If you use the variables passed into a block you are fine as these are locally defined, however, if you create any variables within a block and they just happen to be the same name as a variable in your main script you could accidentally change their values. If you are going to use variables that are not passed into a block define them within the block header using block local variable syntax which will make these declared variables local.
Basically, any variables listed after the semicolon and between the bars becomes a local variable within the block protecting any variable defined in the main body of the program which may have the same name.
You know how I said blocks, procs, and lambdas are all the same, well, not exactly. Blocks, procs, and lambdas are all closures, however, procs and lambdas are specifically instances of the Proc class and therefore not the same as blocks. Many methods, most notably enumerable methods, only take anonymous blocks. If you have a stored procedure that you would like to send to one of these methods use &
before the argument's name when sending it to the method. This basically does the opposite of an &
within a method header, it converts a procedure to a block.
Conclusion
So, to sum up blocks, procs, and lambdas in Ruby just remember that they are basically methods with their own variable scope and variable storage that can be passed into functions or be created by functions. This capability is what makes the Enumerable class possible and so much more. It is powerful stuff worth learning so play around with them in irb be creative and have fun!