1

I was expecting the following snippet:

var2 = "Not Empty" unless defined? var2

to return "Not Empty", but I got nil. Any insight into why this is happening?

Jordan Running
  • 102,619
  • 17
  • 182
  • 182
Shahroz Shaikh
  • 105
  • 1
  • 3
  • 19
  • the one who have down voted it please explain why he did it. – Shahroz Shaikh Nov 05 '15 at 05:12
  • 1
    that was me, and I'm no 'he'. There's no context to know what `var2` is (or isn't) before this line of code. Why do you expect it to return "Not Empty"? – sevenseacat Nov 05 '15 at 05:20
  • The behavior @ShahrozShaikh is describing happens in any context where `var2` hasn't been previously defined. If you put the above code in a Ruby file by itself it does exactly what @ShahrozShaikh says it does. Take a look, it's puzzling: http://ideone.com/ihm2Pt – Jordan Running Nov 05 '15 at 05:31
  • Interesting. It seems to be evident if the variable being set is the same as the variable being checked. If you're setting a different variable it works fine. – mwp Nov 05 '15 at 05:36
  • That doesn't follow at all, @mwp. – Jordan Running Nov 05 '15 at 05:39
  • @Jordan: We are not getting along, are we? What I mean is: `foo = 'bar' unless defined? qux` sets `foo`, but `qux = 'bar' unless defined? qux` does not set `qux`. – mwp Nov 05 '15 at 05:45
  • On the contrary, the latter sets `qux` to `nil`. That's the puzzling part. And sorry if I've been brusque. – Jordan Running Nov 05 '15 at 05:51
  • Ah, indeed it does. No worries, I'm sorry if I've been horribly wrong in eight or nine of my assertions in this thread! (Which I have.) – mwp Nov 05 '15 at 05:52
  • 1
    I don't see an open issue at ruby-lang.org for this finding. Who wants to open one? I'm happy to if there are no other takers. I'll reference this question in the report. – mwp Nov 05 '15 at 05:58
  • 1
    https://bugs.ruby-lang.org/issues/11659 – mwp Nov 05 '15 at 06:48
  • Apparently this question is a verbatim copy of [another question](http://stackoverflow.com/questions/32838805/defined-and-unless-not-working-as-expected) (it's been marked as a duplicate), which makes me wonder if OP is a legitimate user. Regardless, the answers there explain what's going on here, which jives with the comment on the bug you filed. – Jordan Running Nov 05 '15 at 16:55

2 Answers2

2

defined? method will return:

nil => expression not recognizable

The problem in the above snippet is the scope of the local variable. Its end on the line where you using it. To learn more about local variable, please check this: local_variable

pry(main)> p "local_var is not initialized" unless defined? local_var
=> "loca_var is not initialized"

but if you do this:

pry(main)> local_var = "initialized" unless defined? local_var
=> nil

local_var is still nil because its scoped end after that line, so whatever assigned were wasted.

Solution: I will suggest if you want this behaviour then use this one:

local_var ||= "initialized"
Kh Ammad
  • 1,085
  • 9
  • 12
  • 1
    `unless` isn't a scope gate, and indeed if you write it out (as in my answer), the variable is available after the `end`. Additionally, if you do `other_local_var = "initialized" unless defined? local_var`, the variable `other_local_var` is subsequently available! It's only when you try to set the uninitialized variable being checked in a one liner. – mwp Nov 05 '15 at 06:33
  • @mwp I agreed about this line `other_local_var = "initialized" unless defined? `. In-line if the structure has the same behavior as a block. So for the sake example when you map the above code in to block structure: `unless defined? local_var local_var = "initialized" end`. then you can clearly see that **local_var** scope/life_cycle is only in the block. outside that block the value that was assigned no more exist. – Kh Ammad Nov 05 '15 at 09:34
  • @KhAmmad: You are mistaken. A variable initialized in an `if`/`unless` block still exists after the block. Look: http://ideone.com/OpWtlQ – Jordan Running Nov 05 '15 at 15:10
  • thank you jorden for correction. – Kh Ammad Nov 05 '15 at 16:13
0

Try var2 = "Not Empty" if var2.nil? if you're trying to figure out if a variable is nil or not. defined? is used much more rarely and for different purposes (see below).

irb(main):009:0> var2 = nil
=> nil
irb(main):010:0> var2 = "Not Empty" if var2.nil?
=> "Not Empty"
irb(main):011:0> var2
=> "Not Empty"
irb(main):012:0> var2 = 'foo'
=> "foo"
irb(main):013:0> var2 = "Not Empty" if var2.nil?
=> nil
irb(main):014:0> var2
=> "foo"

If you aren't sure whether or not a variable has even been declared, you can use the following syntax:

if defined?(var2).nil?
  var2 = "Not Empty"
end

(It doesn't work all on one line for some strange reason, as @Jordan has pointed out, but this works.)

However, the idiomatic Ruby way to do this, in general, is called a "nil guard" and looks like the following:

var2 ||= "Not Empty"
mwp
  • 8,217
  • 20
  • 26
  • 1
    This also returns `nil` and does not clarify the OP's doubt – code_dredd Nov 05 '15 at 05:07
  • 1
    Ah, my expression was negated. Fixed now. But agreed that it doesn't clarify the doubt. – mwp Nov 05 '15 at 05:10
  • any other solution ? – Shahroz Shaikh Nov 05 '15 at 05:16
  • What's wrong with this one? – mwp Nov 05 '15 at 05:18
  • 1
    If `var2` is nil before this expression, `var2` will be "Not Empty" after this expression. Isn't that what you are trying to accomplish? Don't confuse the return value of the expression with the value stored (or not stored) to `var2`. – mwp Nov 05 '15 at 05:24
  • Down one, up two, is the answer, is not the answer, down one, down two. What the heck is going on here? If someone has a better answer, post it! – mwp Nov 05 '15 at 05:29
  • 2
    This is being downvoted because it doesn't answer the question. OP asked why the behavior exists (when `val2` hasn't previously been defined, *not* when `val2` has previously been set to `nil`). Your answer suggests using `nil?` instead and doesn't even attempt to explain the behavior OP is seeing. – Jordan Running Nov 05 '15 at 05:36
  • Thanks @Jordan. I see what looks to me like a new Ruby programmer and thought to steer him or her away from the `defined?` operator, because it is not likely what he or she is looking for. But I could be mistaken and the OP was actually trying to communicate a well-researched bug in Ruby to the community. :-) I've updated my answer with more examples and better explanations. – mwp Nov 05 '15 at 05:43