If I execute this ruby code:
def foo
100
end
p defined?(foo), foo
if false
foo = 200
end
p defined?(foo), foo
The output I get is:
This is what my pal and Ruby super-expert Josh Cheek had to say:
When Ruby sees the assignment, it initializes the variable in the current scope and sets it to nil. Since the assignment didn't get run, it didn't update the value of foo.
if statements don't change scope like blocks do. This is also the most important difference between
for x in xs
and
xs.each { |x| }
Here's another example:
a = 123 if a # => nil
a # => nil
We shouldn't be able to say if a
because we never set a
, but Ruby sees the a = 123
and initializes a
, then gets to if a
at which point a
is nil
I'd consider it a quirk of the interpreter, really. Gary Bernhardt makes fun of it in wat (https://www.destroyallsoftware.com/talks/wat) with a = a
-Josh
Names on the left hand side of assignments get set to nil
, even if the code can't be reached as in the if false
case.
>> foo
NameError: undefined local variable or method `foo' for main:Object
...
>> if false
.. foo = 1
.. end #=> nil
>> foo #=> nil
When Ruby tries to resolve barewords, it first looks for local variables (there's a reference to that in the Pickaxe book, which I can't seem to find at the moment). Since you now have one called foo
it displays nil
. As Mischa noted, the method still can be called as foo()
.