Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby Blocks, Procs and Local Variables

Tags:

ruby

In Ruby, procs seem to have access to local variables that were present at the time they were declared, even if they are executed in a different scope:

module Scope1
    def self.scope1_method
        puts "In scope1_method"
    end
end

module Scope2
    def self.get_proc
        x = 42
        Proc.new do
            puts x
            puts self
            scope1_method
        end
    end
end

Scope1.instance_eval(&Scope2.get_proc)

Output:

42
Scope1
In scope1_method

How and why does this occur?

like image 820
Ajedi32 Avatar asked Mar 07 '13 22:03

Ajedi32


People also ask

What are Procs in Ruby?

Ruby introduces procs so that we can pass blocks. Proc objects are blocks of code that have been bound to a set of local variables. Once bound, the code can be called in different contexts and still access those variables. You can call new on the Proc class to create a proc. You can use the proc kernel object.

What are Ruby Procs&lambdas?

Ruby procs & lambdas also have another special attribute. When you create a Ruby proc, it captures the current execution scope with it. This concept, which is sometimes called closure, means that a proc will carry with it values like local variables and methods from the context where it was defined.

What is the difference between PROCs and lambdas in Ruby?

Procs don’t care about the correct number of arguments, while lambdas will raise an exception. Taking a look at this list, we can see that lambdas are a lot closer to a regular method than procs are. Ruby procs & lambdas also have another special attribute.

What is block in Ruby?

Ruby blocks are little anonymous functions that can be passed into methods. They are enclosed in a do / end statement (often when the block have multiple lines) or between brackets {} (if the block is a one-liner), and they may have multiple arguments. Block can accept arguments and returns a value. Block does not have its own name.


2 Answers

The Proc.new call creates a closure for the block that it's given. In creating a closure for the block, the block is bound to the original variables in the scope of the Proc.new call.

Why is this done?

It allows Ruby blocks to function as closures. Closures are extremely useful, and the Wikipedia entry (linked above) does an excellent job of explaining some of their applications.

How is this done?

This is done in the Ruby VM (in C code) by copying the Ruby control frame that exists before entering the Proc.new method. The block is then run in the context of this control frame. This effectively copies all of the bindings that are present in this frame. In Ruby 1.8, you can find the code for this in the proc_alloc function in eval.c. In Ruby 1.9, you can find this in the proc_new function in proc.c.

like image 59
sfstewman Avatar answered Oct 06 '22 00:10

sfstewman


This behavior is by design. In Ruby, blocks, procs, and lambdas are lexical closures. Read this blog post for a short explanation of the differences between Ruby's three flavors of closure.

like image 31
dbenhur Avatar answered Oct 05 '22 23:10

dbenhur