What's going on here? What is the subtle difference between the two forms of "unless"?
> irb(main):001:0> foo = true unless defined?(foo)
=> nil
irb(main):002:0> unless defined?(fooo) ; fooo = false ; end
=> false
thx
Apparently, ruby creates local variable at parse time setting them to nil
so it is defined and this is done whether the code is executed or not.
When the code is evaluated at your first line, it doesn't execute the assignment part since foo
is set to nil
. In the second line, because fooo
has not been parsed yet, defined?
returns nil
letting the code inside the block execute and assign fooo
.
As an example you can try this:
if false
foo = 43
end
defined? foo
=> "local-variable"
This is taken from a forum post at ruby-forum.
Let's start with something simpler:
# z is not yet defined
irb(main):001:0> defined?(z)
=> nil
# Even though the assignment won't execute,
# the mere presence of the assignment statement
# causes z to come to life.
irb(main):002:0> z = 123 if false
=> nil
irb(main):003:0> defined?(z)
=> "local-variable"
irb(main):004:0> z
=> nil
Now we can figure out your first example.
foo = true unless defined?(foo)
Is foo
defined? Before we press ENTER in irb
, no. However, the presence of the assignment statement causes foo
to come to life. That means the assignment statement won't be executed, leaving foo
in existence but having nil
as its value. And what is the last expression evaluated in the irb
line? It is unless defined?(foo)
, which evaluates to nil
.
For more info on how assignments (even those that do not get executed) cause variables to exist, see this discussion of Variable/Method Ambiguity.
In your second example, there is nothing mysterious at all: fooo
is not defined, so the code in the block executes, setting fooo
to false
. That assignment is the last expression evaluated, so false
is the return value of our block.
irb(main)> foo = true unless defined?(Integer)
=> nil
irb(main)> foo = true unless defined?(thisIsUndefined)
=> true
Your first block is returning nil
because the way it's written leaves 2 options:
foo
is not defined --> assign truefoo
is defined --> do nothingHere, foo must be defined when the line is evaluated. Thus, nothing happens and nil
is returned.
irb(main)> unless defined?(Integer) ; fooo = false ; end
=> nil
irb(main)> unless defined?(thisIsUndefined) ; fooo = false ; end
=> false
Your second block operates the same way your first one does. If fooo
is not defined, the block is entered and fooo
is set to false
. The result of the last line of the block is the return value of the block, thus the false
you are seeing. If fooo
does exist, then the block is skipped over and nothing happens, therefore there is nothing to return, therefore the nil
.
Based on your code, I would say that foo
was defined when this code was run and fooo
was not (test code shown was generated in Ruby 1.8.6). If you did not define either of these before running this code, then you may have something called foo
that is defined by default (do defined?(foo)
by itself to check). Try using a different name and see if you get the same results.
Edit:
irb(main)> defined?(bar)
=> nil
irb(main)> bar = true unless defined?(bar)
=> nil
irb(main)> defined?(bar)
=> "local-variable"
Apparently, defined?()
is returning true since it has already seen bar
(at the beginning of the line), even though you are still in the process of defining it.
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