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.
1b6ed15a6126e1d5ef4d7d15550f30bb?d=identicon&s=25 François Montel (zerohalo)
on 2007-04-04 21: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.
B3086d7340aeba221e30ce8679eab3f3?d=identicon&s=25 Emmanuel Oga (emmanueloga)
on 2007-04-04 21: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.
852a62a28f1de229dc861ce903b07a60?d=identicon&s=25 Gavin Kistner (phrogz)
on 2007-04-04 21:57
(Received via mailing list)
On Apr 4, 1:25 pm, "François Montel" <zeroh...@gmail.com> 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.
Fd22ee3cfc7dac283ce8e451af324f7d?d=identicon&s=25 Chad Perrin (Guest)
on 2007-04-04 22:13
(Received via mailing list)
On Thu, Apr 05, 2007 at 04:46:12AM +0900, Emmanuel Oga 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.
E7559e558ececa67c40f452483b9ac8c?d=identicon&s=25 Gary Wright (Guest)
on 2007-04-04 22: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
1b6ed15a6126e1d5ef4d7d15550f30bb?d=identicon&s=25 François Montel (zerohalo)
on 2007-04-04 22:57
Thanks, everyone, for the thorough explanation. This is all very
helpful/educational.
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (Guest)
on 2007-04-04 23: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.
E3513c4edd6810bb4b9914b58da2a2c3?d=identicon&s=25 Jamal Soueidan (jamal)
on 2007-04-04 23:43
(Received via mailing list)
http://www.ruby-forum.com/topic/103712

You may want to look at this topic also :)
E3513c4edd6810bb4b9914b58da2a2c3?d=identicon&s=25 Jamal Soueidan (jamal)
on 2007-04-04 23: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
1b6ed15a6126e1d5ef4d7d15550f30bb?d=identicon&s=25 François Montel (zerohalo)
on 2007-04-05 00:12
Jamal Soueidan 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.
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (Guest)
on 2007-04-05 07:35
(Received via mailing list)
On Thu, Apr 05, 2007 at 06:56:46AM +0900, Jamal Soueidan 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.
E3513c4edd6810bb4b9914b58da2a2c3?d=identicon&s=25 Jamal Soueidan (jamal)
on 2007-04-05 11:22
Brian Candler wrote:
> On Thu, Apr 05, 2007 at 06:56:46AM +0900, Jamal Soueidan 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.