Totally awesome software for iPhone and OS X

Tuesday, December 16, 2008

La Philosophie pour les Nul(l)s...

As the dark, cold nights are drawing in I've been throwing logs on the fire, sitting in a comfortable chair with a glass of Cardhu and thinking a little about null values work (or should work) in Java. For example, if you define a method as returning a String, conceptually speaking, what should it do? Should it return only String instances, or should it return either an instance of a String or null.

I've long been used to the way Java happily passes null around and have developed a hell of a lot of code over the years that checks for null, as I'm sure have most people. But, wouldn't it make more sense if the implicit contract of the return type specified by the method were adhered to?

Recently, in some cases, we've been writing what I guess you could call 'null-safe' objects (all fields pre-initialised and setters that don't allow values to be nullified). This on the face of it seems conceptually valid, the methods return what they say they'll return and there's a lot less null checking to do when using them. Objects written like this are ideal for e.g. sticking into freemarker templates as then the template designers don't need to care about checking whether or not values exist because they always do.

Problem solved?

Not really. There is an obvious conceptual difference between zero and nothing for a Long value, or between 'no string at all' and an empty string. And if you're going to 'protect' your objects like this, how do you signal that you've done so to other users of your API?

I think most people are comfortable with a certain synonymity between a null string and an empty string, but it gets trickier for other types (and let's not even get into primitives - which can't ever be null in Java). The problem seems to be that Java has no proper way of handling null other than throwing an unchecked exception when you accidentally encounter it.

Microsoft's venerable .NET has the concept of nullable types which (at least for value types, structs and enums) allows you to specify that a type can, infact, be null e.g. int? j = null; But this, with the exception of allowing nullable value types is little more than a syntatcially nice way of doing null checking (or, indeed the 'null-safe' object approach I identified above).

Is there a better way?

Well, it appears that other languages may have a more cromulent approach. Haskell, Scala etc. avoid the use of null (well, technically Scala does have null, but generally it's not used).

Scala prefers instead the use of a simple 'Option' monad that has two sub-classes 'Some' and 'None'. Option is type-safe, so you can have an Option of any type (but only of the type you define it to be). 'None' is essentially a type-safe null (a singleton object, like Nil in Ruby) and 'Some' is a holder for the actual value. Combined with Scala's filtering and pattern matching, we can explicitly exclude 'None' values from operations or act differently (insert a defult value instead, perhaps) depending on how we want to proceed. I'm no Scala expert, but this does seem a saner approach.

Another similar approach is taken by Objective-C, in which Nil is an object. The nice thing about the way Objective-C handles Nil (and objects in general) is that you can happily send messages to Nil and nothing bad will happen. Unexpected things may happen, but nothing bad :)

Oops *oops = nil;
NSLog(@"Oops is doing %@", [oops doSomething]);

This will probably, from memory, print out something like "Oops is doing (null)", not brilliant. But surely better than a runtime exception?

So, in short, I don't really know what the answer is here... In some cases you need to know about null, nil and empty values and in other cases you don't, that's a given. And you can, kind of, achieve this duality in Java but it's annoying that null itself is a special case. It's not an object, it isn't a proper type and calling methods on a null object throws a runtime exception. You could, I suppose, almost say that this approach was similar to Scala's, in that every java Object is implicity an 'Option', 'Some' is the value (if any) and null is synonymous with 'None' (except it's not type-safe etc. etc. etc.) and you do all the pattern matching and checking yourself and if you don't, or get it wrong the whole thing blows up in your face.

I can't help thinking there could have been a cleaner way to handle all of this.

1 comment:

Anonymous said...

I do like the Scala approach, especially the syntax of matching Some/None and the use of extractor objects.

Looks a bit clumsy in other languages when you don't have the syntax support.

I suppose another common way of dealing with null is to throw an exception if you can't return a meaningful value - difficult for property values though ...

© 2007 Wired Up And Fired Up