Both are terms whose type is the intersection of all types (uninhabited). Both can be passed around in code without failing until one attempts to evaluate them. The only difference I can see is that in Java, there is a loophole which allows null to be evaluated for exactly one operation, which is reference equality comparison (==)–whereas in Haskell undefined can’t be evaluated at all without throwing an exception. Is this the only difference?
Edit
What I’m really trying to get at with this question is, why was including null in Java such an apparently poor decision, and how does Haskell escape it? It seems to me that the real problem is that you can do something useful with null, namely you can check it for nullness. Because you are allowed to do this, it has become standard convention to pass around null values in code and have them indicate "no result" instead of "there is a logical error in this program". Whereas in Haskell, there’s no way to check if a term evaluates to bottom without evaluating it and the program exploding, so it could never be used in such a way to indicate "no result". Instead, one is forced to use something like Maybe.
Sorry if it seems like I’m playing fast and loose with the term "evaluate"… I’m trying to draw an analogy here and having trouble phrasing it precisely. I guess that’s a sign that the analogy is imprecise.
Ok, let’s back up a little.
“undefined” in Haskell is an example of a “bottom” value (denoted ⊥). Such a value represents any undefined, stuck or partial state in the program.
Many different forms of bottom exist: non-terminating loops, exceptions, pattern match failures — basically any state in the program that is undefined in some sense. The value
undefined :: ais a canonical example of a value that puts the program in an undefined state.undefineditself isn’t particularly special — its not wired in — and you can implement Haskell’sundefinedusing any bottom-yielding expression. E.g. this is a valid implementation ofundefined:Or exiting immediately (the old Gofer compiler used this definition):
The primary property of bottom is that if an expression evaluates to bottom, your entire program will evaluate to bottom: the program is in an undefined state.
Why would you want such a value? Well, in a lazy language, you can often manipulate structures or functions that store bottom values, without the program being itself bottom.
E.g. a list of infinite loops is perfectly cromulent:
I just can’t do much with the elements of the list:
This manipulation of infinite stuff is part of why Haskell’s so fun and expressive. A result of laziness is Haskell pays particularly close attention to
bottomvalues.However, clearly, the concept of bottom applies equally well to Java, or any (non-total) language. In Java, there are many expressions that yield “bottom” values:
nullitself, which is well-defined);You just don’t have the ability to substitute one bottom for another very easily, and the Java compiler doesn’t do a lot to reason about bottom values. However, such values are there.
In summary,
nullvalue in Java is one specific expression that yields a bottom value in Java;undefinedvalue in Haskell is a generic bottom-yielding expression that can be used anywhere a bottom value is required in Haskell.That’s how they’re similar.
Postscript
As to the question of
nullitself: why it is considered bad form?nullis essentially equivalent to adding an implicitMaybe ato every typeain Haskell.nullis equivalent to pattern matching for only theJustcase:f (Just a) = ... a ...So when the value passed in is
Nothing(in Haskell), ornull(in Java), your program reaches an undefined state. This is bad: your program crashes.So, by adding
nullto every type, you’ve just made it far easier to createbottomvalues by accident — the types no longer help you. Your language is no longer helping you prevent that particular kind of error, and that’s bad.Of course, other bottom values are still there: exceptions (like
undefined) , or infinite loops. Adding a new possible failure mode to every function — dereferencingnull— just makes it easier to write programs that crash.