Forum: Ruby nil? and non-existent objects

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
François M. (Guest)
on 2007-04-04 23:25
Why is it that the nil? method can sometimes be called on an object that
doesn't exist, and other times will return an error. Under what
circumstances can you call the nil? method on a non-existent object?

Consider:

>> puts x = 1 if x.nil?
=> 1
>> puts ‘z is not defined’ if z.nil?
=> NameError: undefined local variable or method ‘z’

Someone mentioned that = has higher precedence than if, so it could be
that in the first statement x is assigned 1 before the conditional
statement is run. However, x.nil? would then always return false.
Emmanuel O. (Guest)
on 2007-04-04 23:46
I think is because of the ruby's analisis on the scope. That is, in:

puts x = 1 if x.nil?

ruby sees x in the scope even if it is never accesed nor asigned, but in

puts ‘z is not defined’ if z.nil?

z is never in scope, because it is present only inside a string, it does
not appear anywhere before the call of the method "nil?"

you can do:

{ z } if z.nil? => returns nothing of nothings (not nil nor false) ruby
don't complain because z is seen in the scope

Also works:

"HELLO #{an_unique_var_name}" if an_unique_var_name.nil?

-------------------------------------------------------------------------------

François Montel wrote:
> Why is it that the nil? method can sometimes be called on an object that
> doesn't exist, and other times will return an error. Under what
> circumstances can you call the nil? method on a non-existent object?
>
> Consider:
>
>>> puts x = 1 if x.nil?
> => 1
>>> puts ‘z is not defined’ if z.nil?
> => NameError: undefined local variable or method ‘z’
>
> Someone mentioned that = has higher precedence than if, so it could be
> that in the first statement x is assigned 1 before the conditional
> statement is run. However, x.nil? would then always return false.
Gavin K. (Guest)
on 2007-04-04 23:57
(Received via mailing list)
On Apr 4, 1:25 pm, "François Montel" <removed_email_address@domain.invalid> 
wrote:
> => NameError: undefined local variable or method 'z'
Consider this:

  irb(main):001:0> puts x=1 if x
  => nil

  irb(main):002:0> puts y if y=1
  (irb):2: warning: found = in conditional, should be ==
  NameError: undefined local variable or method `y' for main:Object

  irb(main):003:0> if z=1 then puts z end
  (irb):3: warning: found = in conditional, should be ==
  1
  => nil

The difference between them is the order in which Ruby sees the
assignment *when preparing* (syntax parsing), not during execution.
The 'y' and 'z' cases above should run the same way, but due to the
way Ruby checks to determine if a variable exists, it needs to see an
assignment earlier in the file than a reference to the value.
Chad P. (Guest)
on 2007-04-05 00:13
(Received via mailing list)
On Thu, Apr 05, 2007 at 04:46:12AM +0900, Emmanuel O. wrote:
> I think is because of the ruby's analisis on the scope. That is, in:
>
> puts x = 1 if x.nil?
>
> ruby sees x in the scope even if it is never accesed nor asigned, but in
>
> puts ???z is not defined??? if z.nil?
>
> z is never in scope, because it is present only inside a string, it does
> not appear anywhere before the call of the method "nil?"

Is that a spec detail or an implementation detail?  It might be nice to
know whether, assuming a stable language specification, that operational
characteristic might go away at some point.
Gary W. (Guest)
on 2007-04-05 00:20
(Received via mailing list)
On Apr 4, 2007, at 3:25 PM, François Montel wrote:
> Why is it that the nil? method can sometimes be called on an object
> that
> doesn't exist, and other times will return an error. Under what
> circumstances can you call the nil? method on a non-existent object?

It is an artifact of the way the Ruby parser works and the syntactical
ambiguity of a method call with no arguments and local variables.

> Consider:
>
>>> puts x = 1 if x.nil?
> => 1

Here, the Ruby parser 'sees' the expression: 'x = 1' and concludes that
x must be a local variable.  It sees the expression and makes the
determination about 'x' *before* it sees the method call: 'x.nil?'.
Once it has made that determination is considers 'x.nil?' as a method
call against the local variable 'x' and not as a method call for 'x'
relative to 'self'.

The net effect is that the local variable 'x' is willed into existence
by the parser (and defaults to nil).  Since 'x.nil?'
evaluates to true, 'puts x = 1' is executed changing the value of x
to 1, which gets printed.

Consider a situation where x was a method call with an argument:

   puts x = 1 if x('foo').nil?

In this case the parser sees no ambiguity, "x('foo')" must be a method
call even though it already saw 'x = 1' as a local variable assignment.

>>> puts ‘z is not defined’ if z.nil?
> => NameError: undefined local variable or method ‘z’

Here the parser hasn't seen 'z' until it comes across 'z.nil?'.  It
makes the determination that z must be a method call.  When the parsing
is complete and the statement is executed, Ruby tries to call 'z' in
the current context and finds that there really isn't a method
called 'z' and you get the Name Error.

Note that the *parser* can't determine if the method z exists or not.
It is only at the time of execution that that can be determined:

def tricky
   def z; 'z exists!'; end
end

puts z rescue "z isn't defined"    # => "z isn't defined"
tricky
puts z                             # => "z exists!"

Here is another example

if w               # parser sees this as a method call, NameError
                    # exception is raised.
    puts w = 42     # parser sees this as local variable assignment
                    # but it never executes
end

Now consider (in a new session of ruby or irb):

puts w = 42 if w   # parser sees w as a local variable and initializes
                    # w to nil, puts never executes
François M. (Guest)
on 2007-04-05 00:57
Thanks, everyone, for the thorough explanation. This is all very
helpful/educational.
Brian C. (Guest)
on 2007-04-05 01:32
(Received via mailing list)
On Thu, Apr 05, 2007 at 04:25:23AM +0900, Fran?ois Montel wrote:
> Why is it that the nil? method can sometimes be called on an object that
> doesn't exist, and other times will return an error. Under what
> circumstances can you call the nil? method on a non-existent object?
>
> Consider:
>
> >> puts x = 1 if x.nil?
> => 1
> >> puts ???z is not defined??? if z.nil?
> => NameError: undefined local variable or method ???z???

Maybe easier to understand if you consider this:

if false
  x = 0
end

puts x.inspect   # => nil

When the file is *parsed*, the assignment "x = 0" flags "x" as a
local variable from that point onwards within the scope. This is true
whether or not it is actually executed; it's a parse-time decision as to
whether "x" is a local variable or a method call with implicit receiver.

Regards,

Brian.
Jamal S. (Guest)
on 2007-04-05 01:43
(Received via mailing list)
http://www.ruby-forum.com/topic/103712

You may want to look at this topic also :)
Jamal S. (Guest)
on 2007-04-05 01:56
François Montel wrote:
> Why is it that the nil? method can sometimes be called on an object that
> doesn't exist, and other times will return an error. Under what
> circumstances can you call the nil? method on a non-existent object?
>
> Consider:
>
>>> puts x = 1 if x.nil?
> => 1
>>> puts ‘z is not defined’ if z.nil?
> => NameError: undefined local variable or method ‘z’
>
> Someone mentioned that = has higher precedence than if, so it could be
> that in the first statement x is assigned 1 before the conditional
> statement is run. However, x.nil? would then always return false.

You could also fix it this way :)

----------------------------------------------

def x
  @x
end

def x=(val)
   @x = val
end

puts x=1 if x.nil?

----------------------------------------------

This would output: 1
François M. (Guest)
on 2007-04-05 02:12
Jamal S. wrote:

> You could also fix it this way :)
>
> ----------------------------------------------
>
> def x
>   @x
> end
>
> def x=(val)
>    @x = val
> end
>
> puts x=1 if x.nil?
>
> ----------------------------------------------
>
> This would output: 1

Actually, it needs no fixing, or the def statements. The explanation
(thanks, Brian, et al) is that "x=1 if x.nil?" works because when the
parser sees 'x=1' it flags x as a local variable and creates it,
assigning it the value nil. Only then does it execute x.nil?, which
returns a value since x now exists.

In other words, if there is any reference to a new local variable in
your code, even if that reference is not executed (as in Brian's
example), the local variable is created and assigned a nil value.
Brian C. (Guest)
on 2007-04-05 09:35
(Received via mailing list)
On Thu, Apr 05, 2007 at 06:56:46AM +0900, Jamal S. wrote:
> > => NameError: undefined local variable or method ???z???
>   @x
> This would output: 1
I think you should check your work before posting (I do)

Run this:

def x
  puts "calling main.x"
  @x
end

def x=(val)
  puts "calling main.x="
  @x = val
end

puts x=1 if x.nil?

You'll see that your two methods are never called, so they don't make
any
difference.
Jamal S. (Guest)
on 2007-04-05 13:22
Brian C. wrote:
> On Thu, Apr 05, 2007 at 06:56:46AM +0900, Jamal S. wrote:
>> > => NameError: undefined local variable or method ???z???
>>   @x
>> This would output: 1
> I think you should check your work before posting (I do)

true, they are not?
This topic is locked and can not be replied to.