Day 04 - ๐Ÿ”Ž Discover Blocks, Procs and Lambdas in Ruby

Day 04 - ๐Ÿ”Ž Discover Blocks, Procs and Lambdas in Ruby

ยท

4 min read

The concept of Block, Proc and Lambda first came into my life when I started to learn Ruby at Codecademy As my first programming language is Javascript which doesn't have any of these concepts, it's quite confusing while memorising them.

That's why here on day 4, I decided to explain these 3 concepts as clearly as I could.


Block

In Ruby, the word block means the code between two curly braces {} or do-end .

Let's take the each method as an example. You can see the code works in both ways.

array = [1, 2, 3]

# Using {}
array.each { |num| puts num }
# Using do-end
array.each do |num|
    puts num
end

We pass a block into each method as an argument. And each will pass every item into the supplied block to execute puts num .


Proc

What is a proc exactly? We can check the following definition.

A Proc object is an encapsulation of a block of code, which can be stored in a local variable, passed to a method or another Proc, and can be called

With proc, we can save some codes in a variable and then later call it or turn it into a block. It creates flexibility when we want to save our blocks separately.

Let's have some examples here.

Turning Proc into Block

We know the method each can take a block. After the proc puts_num is created, we have to turn it into a block before passing it to each . Using & operator is how we do it.

array = [1, 2, 3]

# Turning a proc into a block by using '&'
puts_num = Proc.new { |num| puts num }
array.each &puts_num

Call()

We can also call the proc directly by using call() . Let's look at the following example.

plus_one = Proc.new { |num| num + 1 }
plus_two = Proc.new { |num| num + 2 }

plus_one.call(1) #=>    2
plus_two.call(1) #=>    3

# shorthands:
plus_one.(1) #=>    2
plus_one[1] #=>    2

Passing into function

The most interesting thing is that we can use the same & operator to turn a proc back into a block.

In the following example, we create a function called puts_num_twice. This function will iterate the array twice and accept an array and also a block.

Inside the function, the first iteration will use call() method on a proc proc transformed from &proc . The second one will transform the proc proc back into a block and pass it to each method.

def puts_num_twice (array, &proc) # this '&' turns the block into a proc
    # Iterate 1 : Pass an actual block to `each` and call the proc `block` indside it
    array.each { |el| proc.call(el) }
    # Iterate 2: This `&` turns the proc back into block and pass it to `each`
    array.each &proc
end

# We pass an array and a block into the function
puts_num_twice([1,2,3]) do |num|
    puts num
end

Arguments in proc

proc has no restriction on the number of arguments. Even though you create a proc to accept 2 arguments, you can pass only one to it and it will execute without any error message.

plus_one = Proc.new { |num, num2| num + 1 }

# These codes work fine
plus_one.call(1, 2)
plus_one.call(1)

Don't put 'return' in proc

When you put a return in a proc inside a function, this will terminate the whole function at that line.

def test_return
  proc { return 4 }.call    # returns from method
  return 5 # this won't be executed
end

test_return #=> 4

Lambda

A lambda function is a general software concept, not specific to Ruby. They are available in many programming languages. A lambda function encapsulates control flow, parameters and local variables into a single package assigned to a variable or used inline.

How to create and call Lambda function?

With Ruby, the lambda keyword is used to create a lambda function. It requires a block and can define zero or more parameters. You call the resulting lambda function by using the call method.

my_lambda = lambda { puts "hello" }


my_lambda.call #=> hello

my_lambda.() #=> hello

my_lambda.[] #=> hello

my_lambda.=== #=> hello

You can also create a lambda with the literal lambda operator, which looks like this -> and can have zero or more arguments.

my_lambda = -> { puts "hello" }
my_lambda_with_args = -> (v) { puts "hello "+v }

my_lambda.call #=> hello
my_lambda_with_args.call("newman") #=> hello newman

The differences between Proc and Lambda

It is really similar to proc except that lambda :

  • is strict on the number of arguments

  • acts differently when there is return inside it

l = lambda {|x, y| "x=#{x}, y=#{y}" }
l.call(1, 2)      #=> "x=1, y=2"
l.call([1, 2])    # ArgumentError: wrong number of arguments (given 1, expected 2)
l.call(1, 2, 8)   # ArgumentError: wrong number of arguments (given 3, expected 2)
l.call(1)         # ArgumentError: wrong number of arguments (given 1, expected 2)

def test_return
    -> { return 3 }.call # just returns from lambda into method body
    return 5 # this will be executed
end

test_return # => 5

Did you find this article valuable?

Support Terry Cheng by becoming a sponsor. Any amount is appreciated!

ย