Null Objects and Falsiness

Thank you to Ben Hamill for sending me a question that prompted this post.

Checking for object presence

Very often in Ruby code, we would like to execute some action only if an object is present:

Strictly speaking, we aren’t checking for object presence here. In Ruby there is almost always an object present, but the default marker for missing data is the special nil object—the one and only instance of NilClass.

Expression Result
h[:missing] nil
noop nil
if (1==2) then 'wrong' end nil

What we are really checking for is the presence of either a “falsy” object (nil or false, most likely nil), or a “truthy” object (anything other than nil or false). So in effect this is an instance of typecasing.

Switching on object type is a smell in object-oriented languages. In general, we don’t want to ask an object what it is; we want to tell it what to do and let it figure out the best way to do it based on its type. This is the essence of polymorphism.

What we need is an object to represent the special case of “do nothing on missing value”. As it happens, there is a pattern for that: Null Object. Quoting Wikipedia: “a Null Object is an object with defined neutral (‘null’) behavior”.

Ruby does not have a built-in Null Object class (NilClass doesn’t qualify). Implementation of one is trivial, however:

Instances of NullObject will respond to any message (method call) with a no-op.

A more useful (and common) form of Null Object returns self from every call.

This version makes it possible to nullify arbitrary chains of method calls:

A useful accompaniment to a Null Object class is a constructor method that converts nil values into null objects, and leaves other values as-is:

We can also define some common conversions a la NilClass:

With these tools in hand we can rewrite our #slug method more cleanly, and, dare I say, more confidently:

In some cases we may want to call methods on other objects if the maybe-null object is not null. For this case, we can define the Ruby 1.9 / ActiveSupport #tap method as a no-op for NullObject:

The code in the #tap block will not be executed if the object is nil.

But is it falsey?

Still, even with a null object replacing nil there may be times when we want to check whether the expected object is present or not.

Hm, that’s not what we wanted. There is no user, but the NullObject standing in for the user is “truthy”, because it’s not false or nil.

This is particularly surprising when we are branching based on the value of a predicate:

What’s going on here? The result of the call to #subscribed? is neither true nor false; it is the NullObject instance. Which, because of Ruby’s semantics, is truthy.

As it turns out, it is not possible in Ruby to make our own objects “falsey”. We can get close:

In a program using ActiveSupport we might also define a few more common predicates:

In Ruby 1.9 we can get even closer to the ideal of user-defined falsiness by implementing the ! (negation) operator:

But still the goal of being able to treat our NullObject like a common nil eludes us.

We might also try basing NullObject on NilClass or FalseClass in order to inherit their falsiness. Unfortunately this too is impossible; NilClass and FalseClass are not allocatable, meaning it is not possible to create new objects of those classes (or any derivative of them).

We could try another tack. We could define a function to “resolve” the null object back to a nil when needed:

Now we when we can wrap our maybe-null object with a Value() call to get the object we need:

If we don’t mind extending core classes we could make this an instance method instead:

Chasing after the wind

Let’s take a step back. Why do we care if the value is falsey? Because we want to handle that case differently. A lot of the time, the way we want to handle the absence of an object is to do nothing, and that’s the strategy that Null Object represents.

But here we want to do something when the value is missing; we want to return a different string. Maybe the problem isn’t really one of making NullObject act falsey. Maybe this isn’t the right scenario for a Null Object at all.

Let’s instead throw together a simple Presenter implementation:

(In a real app the presenters would presumably have many more methods.) Let’s see how the code looks using presenters:

Ah, there we go. Back to good old polymorphism.

Conclusion

If we’re trying to coerce a homemade object into acting falsey, we may be chasing a vain ideal. With a little thought, it is almost always possible to transform code from typecasing conditionals to duck-typed polymorphic method calls. All we have to do is remember to represent the special cases as objects in their own right, whether that be a Null Object or something else.