Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Ruby seem to hoist variable declarations from inside a case statement even if that code path is not executed? [duplicate]

Tags:

We define a function foo:

def foo(s)   case s   when'foo'     x = 3     puts x.inspect   when 'bar'     y = 4     puts y.inspect   end   puts x.inspect   puts y.inspect end 

We then call it as follows:

1.9.3p194 :017 > foo('foo') in foo scope 3 in outer scope 3 nil  => nil   1.9.3p194 :018 > foo('bar') in bar scope 3 in outer scope nil 3  => nil  

Why does the function not throw an error about an unregistered local variable in either case? In the first case, the variable y seems like it should not exist, so you can't call inspect on it in the outer scope; the same for x in the second case.

Here's another similar example:

def test1   x = 5 if false   puts x.inspect end  def test2   puts x.inspect end 

And then:

1.9.3p194 :028 > test1 nil  => nil   1.9.3p194 :029 > test2 NameError: undefined local variable or method `x' for main:Object 

What's going on here? It seems like Ruby is hoisting the variable declaration into the outer scope, but I wasn't aware that this is something Ruby does. (Searching for "ruby hoisting" only turns up results about JavaScript hoisting.)

like image 650
clizzin Avatar asked Oct 17 '12 06:10

clizzin


People also ask

Does Ruby hoist variables?

Last but not least, if you know about hoisting in JavaScript, it's worth mentioning that Ruby's hoisting is much different. In Ruby every variable is available only after the line where the variable has been assigned, regardless if that line will be executed or not.

What does it mean for a variable to be hoisted?

Hoisting is a JavaScript mechanism where variables and function declarations are moved to the top of their scope before code execution. Inevitably, this means that no matter where functions and variables are declared, they are moved to the top of their scope regardless of whether their scope is global or local.

Are variables declared with VAR hoisted?

Variables declared with var are hoisted to the top of the enclosing function scope. If the variable is accessed before declaration, it evaluates to undefined .

Does VAR support hoisting?

Hoisting let and const variables: The answer is a bit more complicated than that. All declarations (function, var, let, const and class) are hoisted in JavaScript, while the var declarations are initialized with undefined , but let and const declarations remain uninitialized.


1 Answers

When the Ruby parser sees the sequence identifier, equal-sign, value, as in this expression

x = 1 

it allocates space for a local variable called x. The creation of the variable—not the assignment of a value to it, but the internal creation of a variable—always takes place as a result of this kind of expression, even if the code isn’t executed! Consider this example:

if false   x = 1 end p x # Output: nil p y # Fatal Error: y is unknown 

The assignment to x isn’t executed, because it’s wrapped in a failing conditional test. But the Ruby parser sees the sequence x = 1, from which it deduces that the program involves a local variable x. The parser doesn’t care whether x is ever assigned a value. Its job is just to scour the code for local variables for which space needs to be allocated. The result is that x inhabits a strange kind of variable limbo. It has been brought into being and initialized to nil. In that respect, it differs from a variable that has no existence at all; as you can see in the example, examining x gives you the value nil, whereas trying to inspect the non-existent variable y results in a fatal error. But although x exists, it has not played any role in the program. It exists only as an artifact of the parsing process.

Well-Grounded Rubyist chapter 6.1.2

like image 164
ck3g Avatar answered Sep 24 '22 02:09

ck3g