Block, Procs and Lambda in Ruby
As a Javascript developer, I was used to calling functions by writing their name followed by a bracket that may or may not take arguments. However, when I switched to Ruby, I had to adapt to a new system where I could call a function without the bracket. One of the most surprising things about Ruby was the ability to pass a block to a function while calling it, and using the 'yield' keyword to execute that block. It was both enlightening and jarring at the same time.
By the end of this article, you'll be able to differentiate between blocks, procs, and lambdas and know when to use procs versus lambdas.
Block
Consider this piece of code
[1,2,3,4].each {|element| puts element * 2}
[1,2,3,4].each do |element|
puts element * 2
end
The above code snippets have the same functionality of iterating over an array and printing each element multiplied by 2. However, the syntax differs, this is how blocks are created in Ruby
In Ruby, a block is a section of code that is enclosed within curly braces {}
or a do-end
statement. Unlike objects, blocks cannot be assigned to variables or passed around independently. They are usually used with methods to provide context-specific behaviour or iterate over collections. Blocks are commonly used with methods that accept them as arguments, such as each, map, select, etc.
Procs
When using Ruby, blocks have some limitations. However, procs are like advanced blocks that can do more. A proc (short for procedures) enables you to make a block reusable and gives it the power to be passed as an argument to methods or assigned to a variable. You can create procs using the proc
method and call them using either the call
or yield
methods, or pass them to a method using the `&` operator.
multiply_by_two = proc { |element| puts element * 2 }
[1, 2, 3, 4].each(&multiply_by_two)
The code above uses proc
to save a block
into the multiply_by_two
variable which is then used in the .each
statement as an argument.
you will notice a special operator &
, this operator is used to convert a proc
into a block and also notice that no argument was passed to the multiply_by_two
when it is called. This is because procs
are closures and have contextual access to variables and data from the environment in which they were defined (the lexical scope). This means that proc
can capture and retain access to variables and data from the surrounding scope, even if they are called from outside that scope.
proc_statment = proc do |statement|
puts "proc statement : #{statement}"
end
def using_yield_for_procs
puts "first method statement"
yield "i am an argument of a proc"
puts "second method statement"
end
#run the proc by using the .call method
proc_statment.call("i am proc with the .call method")
#run the proc by yeilding to it and also passing it as an argument
using_yield_for_procs(&proc_statment)
if you run this in your terminal using irb
the result comes out like this
proc statement : i am proc with the .call method
first method statement
proc statement : i am an argument of a proc
second method statement
looking at the first code, we save a block
of code into a variable proc_statment
by using a proc
, this proc takes in a statement as an argument and prints the statement
below this, we showed two ways in which that proc
statement can be run, first by appending the .call
method to the proc variable and printing out argument
and then by yielding to it inside of a function
In Ruby, a method can take a block as an argument either inline by appending the whole block to the method or by passing the block to the method after conversion to a proc
.
#defining the method which yields(give in) to the passed block
def method_taking_block_as_argument
yield "i am the block"
end
# calling the method and passing the block inline,
# this is weird as a javascript developer but since ruby allows you
# call a function without bracket then this is allowed
method_taking_block_as_argument { |statement|
puts statement
}
# but what if i want the block reusable allover our codebase?
# ruby sent us the power of proc to do just that, now the block
# is reusable and can be passed around
converted_block = proc { |statement|
puts statement
}
# this is how the proc (converted block) is passed into the method
method_taking_block_as_argument(&converted_block)
yield
in ruby (giving up control?)
you might have seen multiple times in the examples where the yield keyword is used and you might have noticed how I have said that as a javascript developer, the concept of not calling a method with a bracket or appending a block to a function call is alien. but this is Ruby and we have the yield
keyword and have the power to call a method without bracket ...
yield
allows us to take control of a function execution and run the block that was passed to the function. it serves as a placeholder within a method where a block can be inserted and executed
looking at the following method
def greet
puts "Hello,"
yield if block_given?
puts "Goodbye!"
end
greet { puts "Welcome to the world of Ruby!" }
Here, yield
acts as the bridge between the method greet
and the block passed to it. When greet
is called with a block, the yield
statement executes the block, resulting in the output:
Hello,
Welcome to the world of Ruby!
Goodbye!
Notice how the block { puts "Welcome to the world of Ruby!" }
is seamlessly integrated into the method's execution flow, thanks to yield
.
lambda
not many people know that arrow functions in javascript are also called lambda functions, and ruby has lambdas.
lambda is a type of proc
, They have a strict syntax and behave similarly to Proc objects. they can be created using the lambda
keyword or the 'stabby arrow' ->
operator, consider the code below
# Using the lambda keyword
my_lambda = lambda { |x| x * 2 }
# Using the stabby lambda operator
my_lambda = ->(x) { x * 2 }
Differences between a lambda and a proc
We earlier established that a lambda
is a type of a proc
but with strict syntax, one difference between the two is in the argument passed to the function.
Argument Checking: A lambda has strict argument checking and would raise an Argument error if the argument passed to the function does not fill the required parameters. while a lambda would raise an error, procs would not, it would just fill in the missing values with
nil
Return Behavior :
procs
with a return keyword are greedy, they do not give control to the function in which they are called in and are used when you want to early return from a function whilelambdas
are very unselfish and always return the execution to its calling function This ensures that execution continues after the lambda invocation.
finally, choose procs
when you need loose argument checking or want to use the return statement to exit early from a method or block. Choose lambdas when you need strict argument checking or want execution to continue after invoking the lambda.
In summary, getting comfortable with blocks, Procs, and lambdas in Ruby is a game-changer for JavaScript developers venturing into this new territory. Whether you're looping through arrays, reusing code snippets, or fine-tuning function behaviour, mastering these tools unlocks a world of coding possibilities. So, roll up your sleeves, dive into Ruby's functional programming features, and enjoy the journey of learning and creating with this powerful language. Happy coding!
Subscribe to my newsletter
Read articles from Justice Otuya directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Justice Otuya
Justice Otuya
I'm Justice Otuya, a seasoned Frontend Engineer with over six years of experience in software engineering. I specialize in building customer-facing web applications and solving complex business problems with innovative solutions.