Variable Scope in Ruby

Resolving a name to either a local variable or method

What happens when you refer to a name which is not yet defined ?

 square_of_sum
 => NameError: undefined local variable or method 'square_of_sum'

 defined? square_of_sum
 => nil

There are two things which I found surprising about defined?.

At first, I expected it to be a method, while it turned out to be an operator. Then on seeing the ? suffix, I expected a boolean response. Which is a common ruby convention. But then defined? is an operator.


defined? 11
=> "expression"

defined? Math::PI
=> "constant"

defined? $_
=> "global-variable"

defined? sum = 1 + 2 + 3 + 4
=> "assignment"

product = 1 * 2 * 3 * 4
defined? product
=> "local-variable"

defined? "never odd or even".reverse
=> "method"

But what about nil itself ?


defined? nil
=> "nil"

Notice that the output is the String “nil”. At this point, except for square_of_sum everything else has been defined.

if false
  square_of_sum = sum * sum
end
=> nil

square_of_sum
=> nil

defined? square_of_sum
=> "local-variable"

Very Interesting! The value of square_of_sum has become nil. We see no NameError like earlier and the square_of_sum is defined as a local-variable.

An expression whose value is nil, is still defined.

But if the assignment will never be executed, how is that square_of_sum is defined ?

You just witnessed lexical scoping in action. Ruby creates the references to variables in code during the compilation step and not during code execution. It sees the assignment and defines square_of_sum as a local-variable.

Resolving a name to variable is independent of the run time call stack and requires only knowledge of the static program source code.

Let us define a lambda which computes the cube of x.

cube = -> { x * x * x }

cube.call
=> NameError: undefined local variable or method 'x'

This looks familiar. We have not yet defined x. Let us define x and try again.

x = 9

cube.call
=> NameError: undefined local variable or method 'x'

The same old error. But isn’t x defined ?

defined? x
=> "local-variable"

Ruby is lexically scoped. When cube lambda was defined, x was undefined. So x is definitely not a local variable. If x was not defined as a local variable then is it a method ? Let us define x as a method which returns the value 9.

def x
  9
end

cube.call
=> 729

This time when we call the lambda it works, which is significant.

In the lexical scope of the lambda cube, the name x is not defined as a local variable. Ruby then makes the distinction that the name x must resolve to a method.

Let us define another variable y and a new lambda for computing cube of y.

y = 11

new_cube = -> { y * y * y }

new_cube.call
=> 1331

This works as expected because when new_cube is defined y is already defined as a local-variable.

Sources

  1. The defined? operator
  2. Ruby Forum: Lexical vs Dynamic Scope
  3. Ruby Forum: Undefined local variable ?
  4. Understanding defined? in lexical scope
  5. Ruby variable scope tutorial
Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s