Block, Procs and Lambda in Ruby

Elevate Your Ruby Skills: Understanding Blocks, Procs, and Lambdas

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 while lambdas 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!