To understand blocks, procs, and lambdas in the Ruby language, one must first understand the concept of a closure.
In programming languages with first-class functions, a function is able to be stored in a variable and passed as the argument to other functions. A closure if a first-class function with an environment- it maps to the variables in existence when the closure is called. A closure can be treated like a variable and you can pass it as an argument to a function or assign it to another variable. Closures store all variables that were in scope when a method was created and allows one to access those variables in another scope.
Ruby does not have first-class functions, however it does have closures; blocks, procs, and lambdas are all Ruby closures. Blocks are used to pass [for lack of a better word] blocks of code to methods and procs and lambdas allow for the storing of blocks of code in variables.
Blocks are chunks of code between brackets or the do and end keywords. A block is able to take parameters at the start of the block enclosed in vertical bars:
In this example, num is the parameter and each integer in the nums array is passed in as an argument with the help of an iterator (in this case #each). The num is multiplied itself (squared) and the result is saved as the variable square, which is then incrementally added to the variable total. Total is then printed to display the result of all of the squared numbers added together. One or more parameters are able to passed into a block, depending upon how it was written.
Similar to the body of a method, a block’s body is stored to be called later. Blocks can be passed into Ruby methods with the help of the yield keyword. To demonstrate the importance of blocks in keeping your Ruby code DRY I will provide a demonstration of the progression of method:
With this version of the say_my_name method, the only thing that is able to returned is the string “Denali” — not very extensible (or DRY if we need to have multiple names printed).
This iteration of say_my_name is a little more abstract and allows for an argument to be supplied when calling the method — displaying that name argument instead of a stagnant string. Although this is a little better in terms of abstraction and extensibility, this method still has some room for improvement. Blocks allow us to give the caller of the method the ability to pass in some code (not simply the argument of name) and the responsibility of the method would be to execute the code. This is also where the yield keyword comes into play.
With this iteration we can call the say_my_name method, pass in some code and the yield executes it. This is cool and all, but what makes blocks so interesting is that you are able to pass parameters to them and receive values from them. The yield keyword is able to take parameters; when you invoke the method you are able to give yield an argument, that argument will be passed to the block and that data will be available to the code within the block.
An important factor to consider here is that yield expects a block to be passed in, so if the method is called without supplying a block an error stating something along the lines of ‘no block given (yield)’ will result. To prevent this, there is a method called block_given? is provided by Ruby, which returns true if a block is passed to the method and false if not.
Another notable feature of blocks is that you are able to explicitly pass in the block as a method parameter and invoke the block inside of that method. This is accomplished with the help of the call method.
Similar to the yield keyword, you are able to pass parameters to the call method.
A Proc object is an encapsulation of a block of code and it is able to be stored in a variable. To create a proc you use Proc.new and pass in a block.
As a proc is able to be stored in a local variable, it can be explicitly passed to a method as an argument, or to another proc, and it can be called.
Additionally, you can use Ruby’s ampersand parameter syntax instead of creating a proc and explicitly passing it to a method. The ampersand is prepended to the argument of the method and converts a passed block to a proc object and store it in a variable within the method’s scope.
Lambdas are very similar to procs with a few differences. Unlike procs and more like a ‘regular’ method, lambdas enforce the number of arguments passed in when called. The lambda keyword is used to create a lambda function, it requires a block and is able to define zero or more parameters. You use the call method in order to call the lambda function.
It is also possible to create a lambda with the literal lambda operator:
If a lambda is called without the specified amount of arguments it will return an ArgumentError. Further, unlike with procs, a lambda treats the return keyword in the same way as a method. Conversely, when calling a proc, control is yielded to the proc’s code block- so when the proc returns, the current scope also returns. If a proc is called inside a function and return is called- the function returns as well. When using a lambda, calling return in the lambda will behave like calling return in a method- so the remainder of the function will execute.
Blocks are commonly used in Ruby for passing pieces of code to function. By using the yield keyword a block can be implicitly passed without having to convert it to a proc. Using parameters prefixed with ampersands results in a proc within the method’s context. Procs are like blocks, but they are able to be stored in a variable. Lambdas are procs, but behave like methods, enforcing arity (the number of arguments taken by a function) and return as methods instead of in their parent scope. How about that for closure.