Update: I've discovered a flaw in my understanding of Ruby's handling of global functions. View the updated entry for a less incorrect explanation.
I'm a little late for my self-imposed weekly blog posting, but I've just discovered something interesting about Ruby's resolution of instance variables.
Here are three Ruby scripts:
Script 1 | Script 2 | Script 3 |
---|---|---|
def foo @foo end class Bar def initialize @foo = 42 end def bar foo end end p foo p Bar.new.bar |
$main = self def $main.foo @foo end class Bar def initialize @foo = 42 end def bar $main.foo end end p $main.foo p Bar.new.bar |
$main = class Foo def foo @foo end end.new class Bar def initialize @foo = 42 end def bar $main.foo end end p $main.foo p Bar.new.bar |
What do you suppose they output?
An experience rubyist would recognise that the first call to #foo will return nil
in all three. The magic of Ruby ensures a few things:
@foo
can always only be an instance variable, so there's none of that foo if (foo=1)
nonsense about resolving methods vs. variablesObject
) so there's no problem resolving the instance variable @foo
in script 1nil
. Practically, this can only apply to instance, class and global variables, because any "sigil-free" variables have to have been assigned for the parser to recognise them as variables.The problem interestingness is in the second call. Bar#bar calls #foo in exactly the same way we just did, so one would expect the @foo
variable to be bound to the main object in the same way. In scripts 2 and 3 this is in fact the case; the explicit receiver (i.e. the $main.
) ensures it. However script 1 is weird.
Because #foo in script 1 is declared as a global method, it is only tenuously bound to the main object. When it is called from inside the scope of a Bar
method, Ruby seems to search that Bar
object's instance variables before stepping out to main's. So the output of the three scripts is:
Script 1 | Script 2 | Script 3 |
---|---|---|
$ ruby script1.rb nil 42 |
$ ruby script2.rb nil nil |
$ ruby script3.rb nil nil |
And now you know.