Table of contents
1.
Introduction
2.
Ruby Blocks
2.1.
Closures
3.
Blocks and closures in relation to each other
4.
Procs
5.
Lambdas
6.
Blocks, procs and lambdas
7.
Frequently Asked Questions
7.1.
What in Ruby is a closure?
7.2.
In Ruby, how do you pass a block as an argument?
7.3.
What distinguishes procs and blocks from one another?
7.4.
When we say that lambda or block forms a closure, what exactly do we mean?
7.5.
How are blocks written in Ruby?
8.
Conclusion
Last Updated: Mar 27, 2024

Blocks as Closures: Using Outside Variables Within a Code Block in ruby

Author Riya Bhogra
0 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

In Ruby Magic, we enjoy delving into the magic that underlies the objects we use daily to comprehend how they function. We'll examine the distinctions between blocks, procs, and lambdas in this edition.

Functions can be stored in variables and passed as arguments to other functions in programming languages with first-class functions. Even other functions can be used by functions as return values.

A closure is an environment-based first-class function. The variables present when the closure was made up are mapped to by the environment. Even if these variables are defined in another scope, the closure will still have access to them.

While blocks, procs, and lambdas are used as closures in Ruby, first-class functions are not present. Code blocks can be passed to methods using blocks, and they can also be stored in variables using procs and lambdas.

Ruby Blocks

Code that can accept arguments and be executed later is captured in blocks.
Blocks are short pieces of code that can be created in Ruby and executed at a later time. Methods that yield blocks within the do and end keywords receive blocks as arguments
Curly braces or the do/end keyword pair can be used to separate blocks in Ruby. They may also function in an anonymous capacity.
A powerful programming technique is to encapsulate behavior in blocks and pass it to methods.
The #each method, which loops over enumerable objects, is just one of numerous examples.

1 [1,2,3].each do |n|
2  puts "#{n}!"
3 end
4 
5 [1,2,3].each { |n| puts "#{n}!" }


A block is passed to the Array#each method in this example, which executes the block for each item in the array and prints the results to the console. 

1 def each
2  i = 0
3 while i < size
4    yield at(i)
5    i += 1
6  end 


In the while loop of this streamlined example of Array#each, yield is called to execute the passed block for each array element. Because the block is implicitly passed to the method, it should be noted that this method has no arguments.

Closures

First-class functions, free variables, and linguistic environment must all be understood to have a clear understanding of what closures are.

A method that can be used as an object and passed as a parameter to another function is a first-class function.

Free variables can still be accessed inside functions even though they are not declared in the function's parent scope.

The visibility of variables is referred to as lexical scoping. The ability to recognize a variable in a program by reading through the code is known as lexical scope, also referred to as eyeball scoping.

Use your interactive console to try this out:

parent_scope = "I'm available everywhere"
3.times do
  inner_scope = "Only accessed in the scope above: -"
  puts "#{inner_scope} #{parent_scope}}"
end


Lexical scoping is demonstrated in the code above. Only the block in which it is defined can see the inner scope. Ruby will throw an exception if you attempt to access it outside the block.

As a result, we can characterize closures as a section of code that stores variables in the environment in which it was created and can be used later. When determining a closure, we apply the following guidelines:

  •        It must serve a function.
  •        Some variables should be referred to in the function body.
  •        It is necessary to declare the variable in a parent scope.

Blocks and closures in relation to each other

Blocks act similarly to anonymous functions in Ruby.

Local variables in blocks prevent variable collusion. This occurs when a global variable shares the same name as a block scope variable.

x = "Global variable"
1.times { x = "Block variable... conflicts and is modified" }
puts x #Block variable... conflicts and is modified


You will receive an unexpected output when you run the snippet above. This is the result of us assigning a variable with the same name to the global scope as we did to the block scope.

To get around this problem, we can add a parameter to the declared block, as shown below:

x = "Global variable"
1.times { |;x| x = "Block parameter prevents variable overriding" }
puts x #Global variable

Procs

A "proc" is a Proc class instance, which contains an execution-ready code block and can be stored in a variable. Call Proc.new and pass a block to create a proc.

1 proc = Proc.new { |n| puts "#{n}!"


A proc can be passed to a method just like a regular argument because it can be saved in a variable. Since the proc is explicitly given in that scenario, we omit the ampersand.

1 def run_proc_with_random_number(proc)
2  proc.call(random)
3 end
4 
5 proc = Proc.new { |n| puts "#{n}!" }
6 run_proc_with_random_number(proc


Ruby's ampersand parameter syntax, which we saw earlier, allows you to use a block in place of creating a proc and passing it to the method.

1 def run_proc_with_random_number(&proc)
2  proc.call(random)
3 end
4  
5 run_proc_with_random_number { |n| puts "#{n}!"


Keep in mind that the method's argument now includes an ampersand. By doing this, a passed block will be transformed into a proc object and kept in a variable within the method scope.

The conversion of a block to a proc results in a performance hit, even though having the proc in the method is helpful in some circumstances. Use implicit blocks instead whenever possible.

Lambdas

With a few exceptions, lambdas are essentially procs. They are more like "regular" methods in two ways: they use "normal" returns and enforce the number of arguments passed when they are called.

Ruby raises an ArgumentError when a lambda that expects an argument is called without one or when an idea is passed to a lambda that does not expect it.

1 irb> lambda (a) { a }.call
2 ArgumentError: wrong number of arguments (given 0, expected 1)
3        from (irb):8:in `block in irb_binding'
4       from (irb):8
5       from /Users/jeff/.asdf/installs/ruby/2.3.0/bin/irb:11:in `<main>


The return keyword is handled similarly by a lambda as it is by a method. The program cedes control to the code block in the proc when calling it. Therefore, the current scope returns if the proc succeeds. When a proc inside a function calls to return, the function also exits immediately.

1 def return_from_proc
2  a = Proc.new { return 10 }.call
3  puts "This will never be printed."
4  end


The proc will gain control of this function, so when it returns, the function also does. The function in this illustration will never print the output and will always return 10.

1 def return_from_lambda
2  a = lambda { return 10 }.call
3  puts "The lambda returned #{a}, and this will be printed."
4 end


It will be printed if a lambda is used. The variable is set to 10, and the line is printed to the console because calling return in the lambda will behave like calling return in a method.

Blocks, procs and lambdas

Let's zoom back out and summarise the comparison now that we've entirely descended into both blocks, procs, and lambdas.

  • Ruby makes extensive use of blocks to pass code to functions. A block can be implicitly given by using the yield keyword rather than converting it to a proc.
  • When passing a block to a method with ampersand-prefixed parameters, the method's context creates a proc. Although they can be saved in a variable, procedures behave like blocks.
  •  By enforcing arity and returning as methods rather than in their parent scope, lambdas are procs that behave like methods.

Frequently Asked Questions

What in Ruby is a closure?

In computer science, a closure is a piece of code that carries the context of its creation with it at all times. Closures in   Ruby are sections of code or methods with variables connected to the scope environment.

In Ruby, how do you pass a block as an argument?

Blocks can be taken by methods in Ruby both implicitly and explicitly. By using the yield keyword in a method, implicit block passing operates. The keyword yield is unique. You don't need to add the block to the list of arguments the method accepts because it finds and calls a passed block.

What distinguishes procs and blocks from one another?

A proc is an instance of the Proc class (observe the lowercase p). This enables us to assign it to variables and call methods on it. Procs may also self-return. A block, on the other hand, is merely a component of the syntax of a method call.

When we say that lambda or block forms a closure, what exactly do we mean?

When a Lambda can access the variables outside this scope, it becomes a closure. I suppose you could say it's magic because it magically can use variables outside of its scope (outer scope) and wrap around the environment it was created in. In other words, if there is a closure, a lambda can access its OUTER SCOPE.

How are blocks written in Ruby?

Curly braces or a do-end statement enclose blocks. Do-end is typically used for blocks that cross multiple lines, whereas for blocks that only cross one line. Arguments for blocks should be defined between two pipe | Characters.

Conclusion

In the hands of a developer, closures can be very effective. These components enable the creation of readable and useful Ruby code. We hope this blog has helped you enhance your knowledge regarding the same. 

Refer to our Guided Path on Coding Ninjas Studio to upskill yourself in Ruby-langRubyCompetitive Programming,Ruby-   documentationRuby FAQs, and many more! If you want to test your competency in coding, you may check out the mock test series and participate in the contests hosted on Coding Ninjas Studio! 

Nevertheless, you may consider our paid courses to give your career an edge over others!

Do upvote our blogs if you find them helpful and engaging!

Happy Learning!

Live masterclass