Null Object For Lists

At RailsConf this year, I saw "Nothing is Something" by Sandi Metz. In it, she explained the Null Object Pattern. The example she used was:

A program looks through the database for a number of animals by their ID, and then prints the name of each animal. In the case that the animal for a particular ID is missing, it had to print "missing animal" instead.

The naive solution was:

animals = ids.map { |id| Animal.find(id) }
animals.each do |animal|
  if animal.nil?
    puts "missing animal"
  else
    puts animal.name
  end
end

The prescription of the Null Object Pattern calls for substituting a "Null Animal" class, which Sandi calls MissingAnimal:

class MissingAnimal
  def name
    "missing animal"
  end
end

Then, a wrapper class GuaranteedAnimal was created which will always return an animal, whether it be a real animal or a "missing" animal:

class GuaranteedAnimal
  self.find(id)
    Animal.find(id) || MissingAnimal.new
  end
end

This allows the original code to be written as:

animals = ids.map { |id| GuaranteedAnimal.find(id) }
animals.each {|animal| puts animal.name }

Notice that we've removed the need for any if statements. In fact, Sandi made a real big deal about not wanting to use if statements. If you watch the beginning of her talk, she goes into how to write if's without if statements, but only via message passing to the true and false classes - a style that Smalltalk uses.

Null Object for Lists

I want to take this even further. Specifically, I would like to apply this principle to the following scenario:

You are writing a search page, and you display the list of results on the page as some <li>'s inside an <ul>. But what happens when no results are returned? To provide good user feedback, you want to display a message saying something like "No results were found."

The obvious way to implement this, usually in some sort of HTML templating language, is to use the equivalent of an if statement. In ERB, this would look like:

<h1>Search Results</h1>
<% if results.size == 0 %>
<p>No results were found.</p>
<% else %>
<ul>
  <% results.each do |result| %>
  <li><a href="<%= result.url %>"><%= result.title %></li>
  <% end %>
</ul>
<% end %>

Pretty standard, right? But, I want to do this without using if statements, can I use the null object pattern here?

It'll take a little thinking outside the box. In order to substitute a method invocation in place of an if statement, we'd have to make it so that in one case, the method is resolved in one class, while in the other case, the message is resolved in a different class. We can do this if the empty list has a different type than a non-empty list. So, we need:

class EmptyList
...
end

class NonEmptyList
...
end

This has implications. It means that if an item were added to an empty list, then its type would have to change to an non-empty list. Can you change the type of an object in runtime? Maybe Ruby can, because Ruby can do pretty crazy things. But, I am not going to go there. What I will do is use an immutable list - a la Clojure, where in order to make a modification to the list, you must create a new list, because the old one ain't ever gonna change. The new one though, could be of a different type and you'd never know it - MUAHAHAHA! In other words:

> list1 = EmptyList.new
<EmptyList>
> list2 = list.append(bob)
<NonEmptyList>[bob]  # list 2 gotten by appending bob to list 1
> list1
<EmptyList>          # original list stays empty

I am going to implement a linked list using this idea. A linked list implementation needs the operations car, cdr, and cons - in LISP speak, but I am going to use these less cryptic names:

This is how it's going to work:

class EmptyList
  def first
    nil
  end

  def rest
    self
  end

  def append(item)
    # if you append an item to an empty list, you
    # get an non-empty list, whose first item is the
    # item, and its "rest of the list" is an empty list
    NonEmptyList.new(item, self)
  end
end

class NonEmptyList
  def initialize(item, rest)
    @item = item
    @rest = rest
  end

  def first
    @item
  end

  def rest
    @rest
  end

  def append(item)
    NonEmptyList.new(item, self)
  end
end

To traverse a list, one would use recursion to iterate down the list through the rest method. For convinience, I will add an each method that works like Array#each. Here's NonEmptyList's implementation of each:

class NonEmptyList
  ...
  def each(&block)
    block.call(first) # call the block for the first item
    rest.each(&block) # defer to the rest of the list
  end
end

For EmptyList#each, it should simply do nothing:

class EmptyList
  ...
  def each
  end
end

Now, that we have that, we can do:

list = EmptyList.new.append(1).append(2)
list.each do |item|
  puts item
end

Sweet! Now that a basic list implementation is done, we can apply the null object pattern and vary the display of the list based on whether the list is empty or not:

class EmptyList
  ...
  def render
    "<p>No results were found.</p>"
  end
end

class NonEmptyList
  ...
  def render
    items = []  # cheating here: convert to 
                # array to use its join method
    each do |item|
      items.push(
        "  <li>\n" + 
        %Q(    <a href="#{item.url}">#{item.title}</a>\n) + 
        "  </li>")
    end
    %Q(<ul>\n#{items.join("\n")}\n</ul>)
  end
end

Now let's exercise these render methods by creating some search results:

Result = Struct.new(:title, :url)
results = EmptyList.new
  .append(Result.new("Null Object Pattern", "http://en.wikipedia.org/wiki/Null_Object_pattern"))
  .append(Result.new("Introduce Null Object", "http://refactoring.com/catalog/introduceNullObject.html"))
  .append(Result.new("Q & A: is nothing something?", "https://van.physics.illinois.edu/qa/listing.php?id=1235"))

Print them out, and...

> puts results.render
<ul>
  <li>
    <a href="https://van.physics.illinois.edu/qa/listing.php?id=1235">Q & A: is nothing something?</a>
  </li>
  <li>
    <a href="http://refactoring.com/catalog/introduceNullObject.html">Introduce Null Object</a>
  </li>
  <li>
    <a href="http://en.wikipedia.org/wiki/Null_Object_pattern">Null Object Pattern</a>
  </li>
</ul>

And now to render an empty list...

> puts EmptyList.new.render
<p>No results were found.</p>

Sweet! We did it, and we did it without using a single if statement. You can find the full source code here.

But...

There is something very troubling about this solution though. What if we wanted to use this linked list implementation to render anything other than search results? We are toast! A broader problem is that we now have presentation code inside of what is supposed to be a general data utility.

But in fact, this problem exists in Sandi's animal example as well. "missing animal" is not the name of any animal. The reason it was chosen as the replacement text is - I would assume - that the programmer knows it will be rendered onto some page where substituting that text would make sense for the user. Therefore, the animal example also has presentation logic inside its model layer.

How can we solve this? Funny you should ask that! That is the topic of the next post. See you then.

blog comments powered by Disqus