Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby variable definition [duplicate]

I stumbled upon a strange behavior in ruby regarding variable definition (and lost a box of donuts on the way):

irb(main):001:0> if false
irb(main):002:1>   a = 1
irb(main):003:1> end
=> nil
irb(main):005:0> a.nil?
=> true
irb(main):006:0> b.nil?
NameError: undefined local variable or method `b' for main:Object
    from (irb):6
    from /Users/jlh/.rbenv/versions/2.1.5/bin/irb:11:in `<main>'

Why isn't a.nil? throwing undefined local variable? Take a look at python, for instance (just wanted to compare it to an interpreted language):

>>> if False:
...     a = 1
... 
>>> print a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined

In a compiled language this wouldn't even compile.

  • Does this mean that ruby is keeping a reference to that variable even if it hasn't gone through that piece of code?
  • If so, how deep are the ifs/else considered for variable definition?

I really can't believe this is the expected behavior in ruby. And it is not irb-specific, running it in a ruby/rails code block gives the same result.

like image 258
jlhonora Avatar asked Feb 27 '15 12:02

jlhonora


1 Answers

In Ruby, there is an ambiguity between referencing a local variable and a message send to the implicit receiver without an argument list. That means

foo

can either mean "dereference a local variable" or "send message foo to self without arguments", i.e. it could either be equivalent to

binding.local_variable_get(:foo)

or

self.foo()
# or
public_send(:foo)

This ambiguity is resolved at parse time. When the parser encounters an assignment to foo, it will, from that point on, treat foo as a local variable, regardless of whether or not the assignment actually gets executed. (That's something the parser cannot determine statically, after all. Just think about if rand > 0.5 then foo = 42 end.)

In a compiled language this wouldn't even compile.

There is no such thing as a compiled language. Compilation and interpretation are traits of the compiler or interpreter (duh!) not the language. Languages are neither compiled nor interpreted. They just are.

Every language can be implemented with a compiler and every language can be implemented with an interpreter. Most languages have both compiled and interpreted implementations (e.g. C has GCC and Clang, which are compilers, and Cint and Cling, which are interpreters, Haskell has GHC, which is a compiler, and Hugs, which is an interpreter).

Many modern language implementations have both in the same implementation, either in different phases (e.g. YARV and MRuby compile Ruby sourcecode to internal bytecode, and then interpret that bytecode), or in a mixed-mode engine (e.g. the HotSpot JVM both interprets and compiles JVM bytecode, depending on which makes more sense), or both (e.g. Rubinius compiles Ruby sourcecode to Rubinius bytecode in the first phase, and then both compiles that bytecode to native code and interprets it, depending on what makes more sense).

In fact, all currently existing Ruby implementations are compiled: YARV and MRuby compile to their own internal bytecode formats, Rubinius, MacRuby, MagLev and Topaz compile to their own internal bytecode formats, then compile that to native code, JRuby compiles to JVM bytecode (which the JVM may or may not compile further), IronRuby compiles to CIL bytecode (which the VES may or may not compile further).

The fact that Ruby behaves this way is because the language specification says so. Not because Ruby is "interpreted", because, actually, it is not. The only purely interpreted implementation of Ruby was MRI and very early versions of JRuby, and both have long since been retired.

like image 167
Jörg W Mittag Avatar answered Oct 04 '22 20:10

Jörg W Mittag