Code

Ruby: Scope and Closures

posted in: Ruby, Software Engineering | 0

Scope

An entity’s scope is the area of the application in which it is “visible.” For example, a variable’s scope is the area of an application that can access the variable. And an object’s scope is the area of an application that can access its methods and attributes.

Suppose we have this code:

Now, let’s see what we can see from where. I’ll attempt to print each variable, both from inside and outside the method.

From this it is clear that the main area can’t see inside the method, and the the method can’t see outside itself into the main area. Therefore, the main area and the method have separate scopes.

Scope and Classes

A class can’t see outside of itself, but a class’s methods and attributes are public, or visible to whatever context instantiates it. Unless otherwise specified, that is: while a class’s methods and attributes are public by default, any of them can be marked private. If they are, then they are only visible inside the class.

Here’s some code to show this. Everything that follows the private keyword is private; the rest is public:

An attempt to call #private_method directly results in a NoMethodError. But a call to #show_private runs without an error. This is because #show_private is public, so we can call it directly. Then #show_private can call #private_method directly, because they are both inside the same class.

We can describe a class’s public scope as out can see in, but in can’t see out.

Closures

A closure is a chunk of code that “encloses” around itself everything that is in scope in the context in which it is created. This scope is called the lexical scope. Everything that is visible in the lexical scope is called the closure’s state. So, we can describe a closure’s scope as the opposite of a class’s: in can see out, but out can’t see in.

When a closure is passed around in its application environment, its original state is passed around with it. All of that original state remains in scope even if it is not in the scope of wherever the closure got passed to.

In Ruby, blocks, procs and lambdas are closures, while methods are not.

Consider this method, which executes a block:

We first initialize and assign the message variable in the main context. Then we call the #test method, specifying a block argument. We have initialized another message variable inside the #test method. We pass that variable to the block when we yield to it. That one is available, then, to the block as the arg argument.

Since blocks are closures, this block can “see” the message variable in main. Furthermore, when the method yields to the block and the block executes, the block can still “see” the message variable, even though the method itself can’t, because message was in scope when we created the block.

In other words, the block carries with itself the state that is in scope where it was written — its lexical scope. So, one definition of a closure is a chunk of code that carries with it the state that is in its lexical scope, even though the method that invokes the closure might not have direct access to it.

Related Articles

This article is one of a series of four, in no particular order. Here are the other three:
Ruby: Blocks
Ruby: Procs, Lambdas and Bindings
Ruby: Block Parameters and Return Values