Thursday, November 29, 2007

Passing and Returning Nulls

Several blog posts about handling null values caught my attention.

The pair of posts from Marty Alchin on "Returning None is Evil" and "Except the Unexpected" offer these opinions:
  • most methods can return null
  • Java has lots of APIs that silently return null (java.util.Map.get(Object key))
  • returned nulls are annoying and hard to debug
  • exceptions should be preferred when a non-null value isn't available
  • always consider when/how nulls should(n't) be used
Cedric Otaku in "It's okay to return null" chimes in with these valid points:
  • don't return exceptions unless something really is exceptional
  • use the Null Object pattern if you need it
Neither of those suggestions (exceptions or Null Object) really work out well for my taste. I really don't like these bad choices:
  1. Write clear and direct code -- that neither shows where nulls could be hiding nor handles them well when they occur
  2. Write lots of verbose if/then/else and try/catch blocks to deal with nulls everywhere
So what to do? Borrow a construct from somewhere else of course!

In the Scala programming language I was introduced to the Option class. I'm pretty sure that is comes from Haskell and other places, but I'm not really researching it.

An Option is like a list of zero or one elements; either a Some with a value, or a None.

Functions that return or receive "optional" things use an Option wrapper to both document that fact and promote cleaner code constructs. Here is an example.

Normal Java code with an override variable:

String overrideMessage;
final String defaultMessage="Howdy";

public String sayHello(String name) {
String s=null;
if (overrideMessage!=null)
s = overrideMessage;
else
s = defaultMessage;

return s+" "+name;
}

Yuck! Now let's uses Option to clean up that method:

Option<String> overrideMessage=none();
final String defaultMessage="Howdy";

public String sayHello(String name) {
return overrideMessage.or(defaultMessage)+" "+name;
}


Much nicer. The Java version can accept alternate values (with the or() method) but not code blocks to execute like the Scala version can (unless Java gets closures...). The Scala version can participate in list comprehensions and all sorts of other things, but I'm not not talking about those things now.

Here is the full version of my Option port to Java:

public class Options {

/**
* An Option can be either full (Some<T>) or empty (None).
* Instead of passing/returning nulls use an Option
*/
public abstract static class Option<T> {
public final boolean some;
public final boolean none;

private Option(boolean some) {
this.some = some;
this.none = !some;
}

abstract public T val();

public T or(T defaultResult) {
if (some)
return val();
else
return defaultResult;
}
}

/**
* Placeholder for empty
*/
public static class None<T> extends Option {
private None() {
super(false);
}
public T val() {
throw new NullPointerException("Can't dereference a None value");
}
}

/**
* A Holder for some value (never null)
*/
public static class Some<T> extends Option {
private final T val;
private Some(T val) {
super(true);
this.val = val;
}
public T val() {
return val;
}
}

/**
* Use this when not sure if value is present or null
*/
public static <T> Option<T> option(T value) {
if (value==null)
return none();
else
return some(value);
}

/**
* Use this to wrap a non-null value
*/
public static <T> Some<T> some(T value) {
return new Some<T>(value);
}

@SuppressWarnings("unchecked")
private final static None NONE=new None();

/**
* Use this when you don't have a value
*/
@SuppressWarnings("unchecked")
public static <T> Option<T> none() {
return NONE;
}
}

1 comment:

John Heintz said...

Just found a reference to this post on Maybe in Java by Tony Morris.

Nearly the same thing, but based on the Haskell-ism.