Null Considered Harmful
c2.com
c2.com
The problem is that unless your language has support baked in (a la Haskell) the syntax has way too much overhead.
If you haven't played with Haskell, I thought this was a pretty good explanation: http://paulbarry.com/articles/2009/07/17/emulating-haskells-...
More generally an option type. Maybe being a monad has other advantages (lifting for instance, though being a monad isn't actually required: C# 3.0 lifts operations into Nullable when you're e.g. adding two Nullable<int> [aka int?] values) but is not a requirement for a "nullable" living in the types.
> The problem is that unless your language has support baked in (a la Haskell) the syntax has way too much overhead.
Not really. The problem is more around the way your generics work: Putting Nothing in a Maybe Int is nice, putting a new Nothing<Int>() in a Maybe<Int> isn't as much.
And if anything, I think having overhead in pushing null values in the system is a good thing: it avoids people running to it right off the bat, and may make them pause and think about better solutions (a command-type pattern, a Null/SpecialCase object, …)
I think there are two ways to deal with this. Either it is known at compile time that a particular variable must never be null. In that case the compiler should catch it like it does with C++ references.
If the decision is left until runtime, the only way to deal with it is to specify or compute a default value, which is very much complicated by the special cased 'this' parameter in OO languages.
It's kind of funny that basically everything Java has removed from C++ turns out to be important for a statically typed language. Java should have been a C++ virtual machine with garbage collection.
It is. Some languages work that way though, Objective-C for instance: `nil` is a "message sink", any message can be sent to `nil` without generating any error, it simply returns nil.
C's NULL isn't an issue either, nil == NULL == 0, and isn't encountered in any of the contexts described by the article.
obj1 = new SomeClass()
obj2 = null
obj2.do(obj1.doWithSideEffects())
So obj2.do being a no-op should mean that obj1.doWithSideEffects is never called. Or should it? [obj1 doWithSideEffects]
will be evaluated before [obj2 do]
because Obj-C is eager, so the runtime doesn't know (or care) about obj2 when it considers the evaluation of the inner expression.To get the behavior you want, you need to use blocks (which incidentally were introduced in 10.6) and write something along the lines of:
[obj2 do: ^{ [obj1 doWithSideEffects] }]
In this case, if obj2 do: doesn't explicitly evaluate the block, obj1 doWithSideEffects won't be evaluated.His 'solution' to the Null Pointer Exception trades that easy to identify and fix runtime error for an entire class of subtle logic errors that are pretty much guaranteed to arise when you have a bunch of improperly-initialized-yet-alive objects sprouting up every time you declare them.
No thank you.
My suggestion is to use an IDE that notices potential Null Pointer Exceptions for you and lets you know about them. VS.NET does this, as does every Java IDE worth its salt. This is simply not an issue if you use modern tools. Please don't go adding language features to "fix" a problem that went away ten years ago.
So NULL can mean dozens of things. You can return something more valid (e.g. "Guest", or even "NotApplicable"), but doesn't get rid of the checks... Can result in more readable code though.
This is a good reference too: http://www.infoq.com/presentations/Null-References-The-Billi... Edit: Plus there is a discussion on this talk here: http://lambda-the-ultimate.org/node/3186
NPE as a "problem" is vastly overrated. Just apply DBC wisely.
if (object == null)
{
throw new IllegalArgumentException("Parameter 'object' should not be null.");
}
Whereas I could lose hours chasing after null values that were passed through layers of unguarded abstractions.Sure, I'd love for DBC to be a language feature (Eiffel has it) but exceptions help me out here, too.
For instance, in a tool that uses graphs as data structures, some transformations might impose that your graph at input and output is acyclic. Don't put this in comments, assert it in code!
The exception is an exception in the strictest sense: something that should not happen. I consider IllegalArgumentException to be an exception you should never catch (except in unit tests, perhaps), but instead fix the root cause. Unlike IOException for instance.
Lisp being mostly a class of dynamically-typed languages, their solution to the issue doesn't really apply to Java. For statically typed languages, the solution adopted by the ML family (as well as haskell) is far superior: encode the notion of "optional" values in an abstract type.