Ruby : Touching The Obj-C Void : nil is nil

A long time ago, in Objective-C on the NeXT, one could often remove nil checks, because all messages to nil would immediately return nil (or 0 depending on the caller’s method signature).

How many times have we seen this in Ruby?:

def foo
  bar && bar.baz && bar.baz.caz("x")
end

Or even worse, avoiding redundant execution?:

def foo
  (temp = @bar) &&
  (temp = temp.baz) && 
  temp.caz("x")
end

In Objective-C this could be written as:

- foo {
  return [[bar baz] caz: "x"];
}

So in Ruby:

class ::NilClass
  def method_missing(*args)
    nil
  end
end

@bar = nil
def foo
  @bar.baz.caz("x")
end

foo
# => nil

Assuming that most of the time bar is not nil, NilClass#method_missing => nil makes for cleaner code that also runs faster than checking for nil along the way.

An additional benefit is that nil can also be used as an immutable empty collection sink by defining NilClass#size => 0, NilClass#empty? => true, etc.

Obviously, it breaks code that expects exceptions to be thrown for messages to nil.

Introduce a method that explicitly checks for nil:

module ::Kernel
  def not_nil; self; end
end

class ::NilClass
  def not_nil; raise("not_nil failed"); end
end

@bar = nil
def foo
  @bar.baz.caz("x").not_nil
end

foo 
# => RuntimeError: not_nil failed

Comments?

Leave a Reply

Your email address will not be published. Required fields are marked *

*