4 ms·
The problem with NullPointerException is that it usually comes with a high error-failure distance (that is, the failure happens later than the logical error tha
by michaelochurch 12y ago
The problem with NullPointerException is that it usually comes with a high error-failure distance (that is, the failure happens later than the logical error that caused it). It can be extremely hard to debug, because null is actually an overloaded concept. It ends up meaning:
1. Like NaN, the result of an error (in some languages).
2. A pre-initialization value that "should never appear" by informal contract.
3. Missing data (especially in databases).
4. Not-applicable.
5. "There is none" (not quite the same as #3 or #4).
6. A return on an unusual (and bad) condition, like a malloc that can't.
7. (Infuriatingly common.) Errors in programming.
8. Empty collections (from Lisp, although that's probably a design error
and Clojure's moving away from it.)
I consider a NullPointerException for any reason to be a bug in a Java program. A more useful error message should be given. I also consider null in Scala to be something that ought to be disallowed. (In Clojure, you can't be as hard-assed about it, because nil is actually useful. But I'd still argue that NPE is always an error.)
Zero-as-null is pretty awful, I agree. When you see that, it's usually as a performance optimization. For example, prices on exchanges are usually represented as integer numbers of pennies (or some smaller "microcurrency" unit) for regulatory reasons (accuracy standards set in decimal terms, disallowing floating point) and the data is unboxed for performance reasons, requiring some "illogical" sentinel value when the number isn't applicable.
The effect of that is to externalize complexity to the client. For example, if you use "0" for "no bid" that's usually harmless because "no buyer" and "highest buyer at $0" are effectively equivalent. But 0 is also used for "no offer" and (if the client isn't aware of the 0's meaning) might make incorrect conclusions, since an ask of 0 (commodity given away for free) means the opposite of "no ask". The client is forced to keep track of this extra complexity-- that 0.0 doesn't mean a numerical value but means there is none.
Null policy (over all the different causes of "null") is really about how the complexity is externalized to the client. Zero-as-null forces the client to deal with "zero is not zero" possibilities, which can be deadly. The best, if the API can support it, is to use Haskell-style optional types (Maybe). You can't get away from the intrinsic complexity, but you can at least require in the type system that the programmer deal with it. (Non-exhaustive pattern-match warnings should generally be treated as errors.)
The problem with, say, Java's references, is that every object type has null as an additional value. Instead of a Spoon, what you actually have is an Maybe (IORef Spoon). All your (non-primitive) types end up being optional, but in a way that is usually sparsely documented.
- dllthomas 12y ago"I consider a NullPointerException for any reason to be a bug in a Java program." Is there actually anyone who disagrees on that point?
- michaelochurch 12y agoSurprisingly, you see that sort of issue (perhaps in a more subtle way) a lot, even in evolved libraries and languages. By "I consider NullPointerException for any reason to be a bug" I mean that, even if the program is going to crash hard, it should give a better error message. Crashing hard is sometimes the right thing, but high error-failure distance is never good. This may have changed, but for a while, OCaml raised the dreaded (and useless) Not_found error on List.head [], and it wasn't until 3.10 that you got a stacktrace on a runtime error. (Of course, most people who use OCaml or Haskell in production rename those functions to, e.g., unsafeHead and make the optionized versions the default.) Debugging and error-handling culture has come a long way, even in the past 10 years. Clojure, for example, is a great language and I am overall a huge fan, but it was painful to debug when it first came out, and it used to be fairly common for runtime errors to manifest as NullPointerException.