Ruby: Nil Aint Nothing

Published: Sat 29 July 2017

In Blog.

A kind-of universal “truth” that one becomes accustomed to as a programmer is that null aka NULL aka None means “nothing”:

#include <stdio.h>

void main(int na, char** args) {
     printf("The value of NULL: %p\n", NULL);
$ cc test.c && ./a.out
The value of NULL: (nil)
$ csharp
Mono C# Shell, type "help;" for help

Enter statements below.
csharp> Console.WriteLine("Null is: " + null);
Null is:
$ python -c 'print "None is: " + str(None)'
None is: None

Also, one has come to accept that null/NULL/None is “type-less”, i.e., is something unique, and certainly not classed with ordinary objects.

csharp> typeof(string);
csharp> typeof(null);
(1,12): error CS1031: Type expected
Python>>> type("astring");
<type 'str'>
Python>>> type(None);
<type 'NoneType'>

OK, so Python seems to think that None has a type, which means that None is an instance of NoneType; let's confirm that:

>>> t = None.__class__
>>> isinstance(None, t);
>>> dir(None)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__',
 '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
>>> None.__repr__()
>>> None.__hash__()

Therefore, in Python, None is an object with a type. As it so happens, there is only one instance of NoneType which is none other than ... None.

Now, I can only assume therefore that None is a singleton, and will always have the same hash. Let's see if the implementation of Python on my Raspberry Pi comes out with the same value (I would expect it to):

$ python
Python 2.7.9 (default, Sep 17 2016, 20:26:04)
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> None.__hash__()

Nope - seems the hash of None is something that is not universal—at least it's the same across invocations of the interpreter.

Ruby OTOH, has some noteworthy, slightly “off the beaten track” qualities to it's own version of ... nothing, that is: nil. I was going to pull a rabbit out of the hat and surprise you with it being an object, with a type, but Python ruined that party.

As it so happens though, Ruby's nil does have some features that make it interesting, and arguably enhance its status beyond the mundanity of C, C# and Python's equivalents:

On my x64-based Ruby implementation:

$ cat /etc/issue
Ubuntu 14.04.5 LTS \n \l
$ irb
irb(main):001:0> nil.object_id
=> 4

4? How does the object ID (essentially the “key” for objects in Ruby) end up being 4? Is this universal? Let's try the version on my Raspberry Pi:

$ cat /etc/issue
Raspbian GNU/Linux 8 \n \l
$ irb
irb(main):003:0> nil.object_id
=> 4

So it turns out that there's an interesting trick that gets used under the hood with certain values in Ruby. It's a bit long-winded (but interesting in an “under-the-hood-gems” kind of way), suffice to say that for certain simple values, notably true, false, nil and Fixnum's, the object_id is always the same across implementations:

irb(main):001:0> true.object_id
=> 2
irb(main):002:0> false.object_id
=> 0
irb(main):003:0> nil.object_id
=> 4
irb(main):004:0> 0.object_id
=> 1

Comments !