A Healthy Respect of Null
I recently traveled from the over-populated World of Java into the beautifully rustic Land of (professional) Ruby. Like any traveler, I had a few companions (ideologies, habits and conventions) which kept me safe and sound in Java's harrowing landscape. But when I left Java for newer pastures some of those companions trailed along. One of them is named Scary Null.In Java -
null is not an object - just ask it:null instanceof Objectwill be false. Worse yet, attempting to use it for anything other than a place-holder and he'll throw his even scarier friend
NullPointerException at you. Over the years I learned a healthy respect for null. He has one and only one job: to hold the place of something. How dare I use null in a capacity for Which He Was Not Intended!A Healthy Disrespect of Nil
In Ruby Land, however, he has a cousin:nil. But nil is not a fearful beast like null. Nil is a charming little scamp, a first class object that does her best to help you out. This took some getting used to for me. I'm so used to writing code like this in Java:int i = str == null ? 0 : Integer.parseInt(str);that it almost feels awkward to do it the Ruby way:
i = str.to_iIn the back of my mind I think: "Eric, what are you doing! What if 'str' is nil? I'll get an exception!" Au contraire. Since
nil is a real object, it doesn't throw exceptions - nil is an object of the type NilClass which implements plenty of useful methods, like:- &
- |
- ^
- nil?
- to_a
- to_f
- to_i
- to_s
- to_yaml
nil represents: false. That makes code like this possible:c is true. No exception. If I was smacked upside the head with a board and the ensuing brain damage caused me to want nil to throw an exception in this situation, I could always override the method in nilThe other methods are equally kind.
nil.to_s (to string) returns "" (empty string), nil.to_a (to array) returns [] (empty array), and my favorite nil.nil? return true - and is the only object that does.Bring on the Fun
Nil is fun, but so what? If I attempt to use a method that doesn't exist, I'll still get an exception! True. However, since we know thatnil is a real object, and since Ruby can override object implementations on the fly (weee!) we can also add methods to nil.Consider the following code, that iterates until a string is constructed that is over 10 character long, then returns the string:
It outputs:
laDid you see something wrong with that picture? Not if you are from the World of Java. Initialize a variable, iterate, append and finally return the value on the correct conditions. But:
lala
lalala
lalalala
lalalalala
lalalalalala

Why declare a variable that we only use inside the loop and return? Why indeed. Let's try it again without declaring the variable.We get an error :(.
NoMethodError: You have a nil object when you didn't expect it!Oh - it looks like Ruby requires us to define variables first. And since
You might have expected an instance of Array.
The error occurred while evaluating nil.+
str += "la" is the same as str = nil + "la" when str is not yet defined, it makes sense that it complains.But wait! Re-read the last sentence. Of course! Ruby thinks that
str is nil, and is attempting to append the string "la" to it. So what would happen if the nil.+ method was declared? What if it just returned whatever was passed to it?def nil.+(o)Now we're cooking with gas! Try and run the above code now. It works(!) since our
o
end
nil.+ method just set str to "la". For each successive loop, str just uses the standard string append method instead.There are a few methods I like to add to
nil. Just create a file named "safe_nil.rb" in your library, and add it like any requires.Now you can safely do things like this:instead of
11 comments:
Very clever! If I may, I'd like to suggest these as well:
# Behaves like an empty array when pushed
def nil.push(o); [o]; end
# Behaves like an empty string when appended to
def nil.<<(o); o; end
I was attempting to avoid anything that would represent that the nil had any state.
"a = nil + 1" makes sense that "a" would be set to "1", "nil" is just an alias for "0".
But "nil << 1"? Seems misleading. Maybe I just don't get it? That's possible.
nil.to_i is equal to 0?
Why not -1?
Sorry, but that sounds to me like provoking all kinds of hidden errors.
I'm glad that I'm still working in Java, where we have a real null value.
th eimage http://bp3.blogger.com/_E3_4LXYT1LI/RtZHLnSBZ_I/AAAAAAAAAEo/V8ctJ6Devn4/s1600-h/ruby.png is brilliant :-)
OMG!
Clever but error prone.....
Null or Nil are markers of UNKNOWN.
SQL handles null values the best way...
Java's null's are nonexistent pointers, and nothing else. A legacy from C and 0 pointer(NULL pointer).
And BTW nil == 0 must be false, and so must be nil.to_i == 0. 0 is an integer why the inconsistency?
Sure you are just hacking, but this may be a really destructive idea...
BTW you kick the cup, witch way are you kicking? Just like everything else in Ruby 1000 ways to do the same thing...
Anonymous:
I never set nil.to_i == 0. That is a built-in value of NilClass in Ruby. All I wanted to note was that if Ruby is going to support the idea of nil as 0/false -- why not extend it?
JAlexoid:
Nil will always be a marker of "unknown" in Ruby, since it is the obly object that returns "true" for "nil?".
If you didn't notice he's kicking a Java cup - representative, see? The bad habits Java people (myself included) bring into Ruby have to go.
Eric:
See my problem is that unknown cannot be 0. Can't the "nil?" method be overridden?
But basically I like the idea, that null's and nil's are not correctly defined for most languages. SQL is closest and Ruby is on the right track.
I know what the cup represents :)
Who ever said that Ruby's nil is "unknown"??
Wrong. In Ruby, nil is nil. The value of nil is nil. (no value, so it is written as nil)
If you convert it to an integer, it's value will be 0
Makes sense, since 0 is the value of nothing!
Convert to a float, same, 0.0
convert to a string, "", empty string object!
to array? [] empty array object!!
These things make sense. To Matz. And to me!
On top of that, they're very convenient.
Not convenient for certain forms of mathematics ? or for your sense of logic? Then use a different class or redefine it!
Beauty of Ruby.
Yikes. Some scary ruby in this post.
With methods like that for your nil objects - one can soon find himself chasing bugs that don't normally exist.
Nil is nil. It is also completely incorrect to say that nil is false.
nil.class # => NilClass
false.class # => FalseClass
true.class # => TrueClass
Yes a nil when considered in an integer context might be a 0, and when considered in a boolean context might be a false. But nil is none of these.
nil is an instance of the NilClass, and there are methods to that class that conveniently allow to create other objects. nil.to_i will will create a Fixnum object with the value of 0, it really does not mean that nil is 0, when using nil.to_f you get a Float instance with the value of 0.0, and nil is not a float either.
When someone says that nil is 0, or that nil is false, or that nil is "", or that nil is 0.0 - that is completely incorrect. Perhaps in C/C++ NULL was defined as 0 or -1 and vice-versa. In Ruby, nil is nil.
Regarding jimbojw's methods. I think they make complete sense. nil.to_a returns [], so nil.push(o) should behave like [].push(o).
Ditto for strings. I love it!!
For all of the people worried about nil, keep in mind that you wouldn't want to test nil == 0, but you may want to test that a == 0, when a is meant to be an int, but is really undefined. I think Ruby's implementation for nil is very nice. It allows the same behaviour as SQL, where there is a nil object that is not true or false but represents the absence of a value. nil != false, and false.nil? is false. It is it's own unique value while still being a real object and not allowing for a NPE. To anonymous: Java's null value isn't really a real value. That is why you get a null pointer exception.
It looks great for me! But I think you can go even a step further. Different classes can expose different specialized behaviours on specific methods.
For this reason, with the help of Java's static type checking, you should be able to deal with NULL as particular values for every class, instead of a particular built-in NilClass object.
Explained in: Null variables invocations
Post a Comment