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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With