Is this A Good Thing or not?

by Gregory Brown

I've always liked the fact that nil evaluates to false. This is handy in conditional expressions and makes ||= possible.

However, something I didn't expect is a bit annoying:
nil.to_i gives you a 0, which evaluates to true in ruby.

So stuff like this happens:

irb(main):002:0> puts "hello" if nil.to_i
=> nil

At a first glance, it seems like zero might have been better replaced by nil or an exception.

Does anyone have any insight as to why nil.to_i returns zero and why that might be more useful or better than an alternative solution?


Pawel Szymczykowski
2005-12-26 19:38:44
I don't get it - what you you expect to happen? You're asking to convert to an integer, so nil -> 0 seems pretty reasonable to me. Then if you want to test for a nonzero value, what's wrong with if nil.to_i > 0 or else unless

If you ask me or Martha, it's a good thing.

2005-12-26 20:47:04
The conflicting code in question:

number = ARGV[1].to_i || 2

I need to preserve ARGV[1] actually being 0 if it occurs, and if it's nil, I don't want it to be 0.

I rewrote it as number = ARGV[1] ? ARGV[1].to_i : 2 but there goes the DRY principle.

Also, imagine a situation where nil.to_i is a divisor. I rather get a real exception than a 'divide by zero' which could have weird results.

I'm curious as to where nil.to_i -> 0 is A Good Thing.

nil is NOT zero. This is coming more from a mathematical point of view but undefined and zero are not even remotely the same thing.

It's easy enough to work around, though.

Pawel Szymczykowski
2005-12-27 00:01:01
I think I may have replied without fully grasping what you were posting about - sorry about that.

Still, I don't think nil.to_i -> 0 is bad.. maybe mathematically, but it is pretty darned convenient more often than not I think. Especially when pulling possibly nil rows from a database I'd like to be integers. Maybe it's because I come from a Perlish background, but I've gotten to relying on that behavior to spare me from writing a rescue or two. Admittedly not a good habit I think, but brevity has its virtues too. :)

How about..

number = ( ARGV[1] || 2 ).to_i # Whole different kind of redundancy here!

You could always use '2' or :2 if 2.to_i is too weird, but I personally get a kick out of the fact that it's implemented. So yeah.. just ignore that - it's late over here.

2005-12-27 00:56:50
Hmm... I like your suggestion for number = (ARGV[0] || 2).to_i. This works and is reasonably clear and concise.

As far as the underlying issue of nil.to_i -> 0, I am sure it's useful somewhere. The database example is a good one. However, I'd be much happier seeing that be the default behavior of RubyDBI than for the language to throw a surprise at you when somehow something false becomes true.

It's an easy fix on either end, but I just prefer consistency over convenience when it comes to the language itself. :)

2005-12-27 01:55:48
You talk about consistency. For me, consistency means, that when I ask Ruby to give me an integer, it gives me exactly that. Not nil nor (God forbid!) an exception.

And if I ask for an integer of nil, 0 seems to be just the right answer.

2005-12-27 02:08:26
I guess it depends on how you look at it. To me, true, false, and nil represent state, not value.

In the true and false cases, Ruby does not define to_i

Yet, nil, which is the representation of the undefined state, gives you a definite value of 0. Handy, maybe. In some cases.

Consistant? Not at all. Of course, I am not expecting to see a change to this, if i did I'd be writing an RCR for it. I'm just curious if anyone knows why the ruby core team decided to implement nil.to_i

I suppose that the same can be said for nil.to_f and nil.to_s so maybe my mathematical mindset is overshadowing the obvious in this case.

If 0 evaluated as a non-true value in ruby, I would not find an issue here. I'm glad that it doesn't, so I guess I can live with a bumpy thing like nil.to_i and friends.

Pawel Szymczykowski
2005-12-27 02:22:06
It's a little bit beside the point, but one of the reasons I LOVE Ruby is because it's so easy to do even slightly subversive stuff like the following (if you really wanted to):

irb(main):002:0> nil.to_i
=> 0
irb(main):003:0> class NilClass
irb(main):004:1> def to_i
irb(main):005:2> nil
irb(main):006:2> end
irb(main):007:1> end
=> nil
irb(main):008:0> nil.to_i
=> nil

Now that's one heck of a language.

2005-12-27 02:35:09
Haha! I just finished the following in IRb right before seeing your comment:

sandal@karookachoo:~$ irb
irb(main):001:0> class NilClass
irb(main):002:1> def to_i; end; def to_s; end; def to_f; end
irb(main):003:1> end
=> nil
irb(main):004:0> @a.to_i || 2
=> 2
irb(main):005:0> @a = 4
=> 4
irb(main):006:0> @a.to_i || 2
=> 4
2005-12-27 05:42:01
>>if you really wanted to
class NilClass; def to_i; end end

What is the rule of thumb for popping in changes to core classes like that? Do it freely? Do it relunctantly? Should we worry upfront about the conflicts they might cause, or just deal with the conflicts when/if they ever happen?

2005-12-27 08:06:04
Rubyists are generally pretty liberal when it comes to opening up classes. It's often one of the coolest (and easiest) ways to solve a problem. However, it certainly depends on your application. If something can be done as easily without modifying a core class, it's probably a good thing to do so so as it lessons the chances of side effects. Still, with proper unit tests and a little forethought, popping open classes (even core classes) can often save time and be very elegant.
Michael Granger
2005-12-27 10:40:58
I think the rule of thumb is that if you're writing something for reuse, do it reluctantly, but if you're writing something just for yourself then do it freely.
2005-12-27 10:41:17
I think ruby considers everything as an object. So the only non-object value which is false is 'nil'. What nil.to_i gives is an object of integer object which means the presence of an object. nil is the only way to tell ruby that its a false object and my personal opinion is that this is good.
2005-12-27 11:28:01
nil is an object.

irb(main):001:0> nil.class
=> NilClass
irb(main):002:0> nil.methods
=> ["&", "object_id", "singleton_methods", "equal?", "taint", "frozen?", "instance_variable_get", "kind_of?", "to_a", "respond_to?", "type", "protected_methods", "|", "eql?", "instance_variable_set", "hash", "is_a?", "to_s", "send", "class", "tainted?", "private_methods", "^", "__send__", "untaint", "id", "to_i", "inspect", "method", "instance_eval", "==", "===", "clone", "public_methods", "extend", "freeze", "display", "to_f", "__id__", "=~", "methods", "nil?", "dup", "instance_variables", "instance_of?"]

Also false is a false object which is of the class FalseClass

Ross Karchner
2005-12-28 18:10:35
I'm more annoyed that 0 evaluates to true-- Python's behavior that 0 and empty sequences evaluate to false always seemed pretty reasonable.