5

As in this question, when a local variable not defined is used within its own assignment, it is evaluated to nil.

x = x # => nil 

But when the name of a local variable conflicts with an existing method name, it is more tricky. Why does the last example below return nil?

{}.instance_eval{a = keys} # => []
{}.instance_eval{keys = self.keys} # => []
{}.instance_eval{keys = keys} # => nil
Community
  • 1
  • 1
sawa
  • 165,429
  • 45
  • 277
  • 381

3 Answers3

13

In Ruby, because methods can be called without an explicit receiver and without parentheses, there is a syntactic ambiguity between a local variable reference and a receiverless argumentless method call:

foo

could either mean "call method foo on self with no arguments" or "dereference local variable foo".

If there exists a local variable foo in scope, this is always interpreted as a local variable dereference, never as a method call.

So, what does it mean for a local variable to "be in scope"? This is determined syntactically at parse time, not semantically at runtime. This is very important! Local variables are defined at parse time: if an assignment to a local variable is seen by the parser, the local variable is in scope from that point on. It is, however, only initialized at runtime, there is no compile time evaluation of code going on:

if false
  foo = 42 # from this point on, the local variable foo is in scope
end

foo # evaluates to nil, since it is declared but not initialized

Why does it make sense for local variables to "shadow" methods and not the way around? Well, if methods did shadow local variables, there would no longer be a way to dereference those local variables. However, if local variables shadow methods, then there is still a way to call those methods: remember, the ambiguity only exists for receiverless argumentless methods calls, if you add an explicit receiver or an explicit argument list, you can still call the method:

def bar; 'Hello from method' end; public :bar

bar # => 'Hello from method'

bar = 'You will never see this' if false

bar # => nil

bar = 'Hello from local variable'

bar      # => 'Hello from local variable'
bar()    # => 'Hello from method'
self.bar # => 'Hello from method'
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
1

Short answer is, because Matz defined it so. This behavior is one of the very few things I don't like about Ruby. It even gets better:

a = b if a
=> nil
a
=> nil

Variable a gets initialized to nil even though in theory a = b statement should under no circumstances be executed.

Boris Stitnicky
  • 12,444
  • 5
  • 57
  • 74
  • Well, in practice it's not executed either. If it actually got executed, then `a` would have the value of `b`; but it doesn't. What looks like execution is actually a scope change - if the expression `a` is referenced after `a = b`, it refers to the local. In the same way, the expression `Object` means something different at the top-level vs inside the module in this case: `module Foo; class Object; end; end`, due to differing scope. See Jörg W Mittag's answer. – Kelvin Oct 01 '13 at 15:29
  • Kelvin, you are mixing two unrelated things. You are right on the first one. `a = b if a` is actually too complicated an example. The weirdness is eg. that `a if a` fails, while `a = a if a` initializes `a` to nil, even though the contents of the `if` statement should not matter, since it never gets executed. I understood that even though it never gets executed, the compiler goes through it _in the left to right order_. So eg. `if b then b = b end` fails again. But as for `module Foo; class Object end end`, this is actually not an issue of _scope_, but one of _namespace_ -- you are simply... – Boris Stitnicky Oct 02 '13 at 05:03
  • ... defining a constant on the module `Foo`. It can be equivalently rewritten as `Foo = Module.new; Foo::Object = Class.new`. – Boris Stitnicky Oct 02 '13 at 05:04
  • Perhaps my example wasn't the best. Look at [this answer](http://stackoverflow.com/questions/2687276/scope-of-constants-in-ruby-modules/11677867#11677867), where I mention the pitfall. The inner module can't access the outer module's constant w/o a namespace when the inner isn't lexically enclosed by the outer. Some people might consider this odd, just like the auto-initialization of locals. But I guess my real point is, I'm not quite sure why you "don't like" the locals behavior - is there a real world use case where you'd actually want to get a `NameError` on `a`? – Kelvin Oct 02 '13 at 16:03
-3

I think in your case it's because it's what's expected :P

1.9.3-p194 :001 > {}.instance_eval{a=1}
 => 1 
1.9.3-p194 :002 > {}.instance_eval{a}     
NameError: undefined local variable or method `a' for {}:Hash
    from (irb):2:in `block in irb_binding'
    from (irb):2:in `instance_eval'
    from (irb):2
    from /Users/rafael/.rvm/rubies/ruby-1.9.3-p194/bin/irb:16:in `<main>'

Instance eval evaluates code at instance level so each hash you are declaring is different. If you want to return keys this works

1.9.3-p194 :003 > {}.instance_eval{keys = self.keys
1.9.3-p194 :004?>   keys = keys}
 => [] 
Rafa de Castro
  • 2,474
  • 1
  • 24
  • 44