

Why we should love null - javinpaul
http://www.codeproject.com/Articles/787668/Why-We-Should-Love-null

======
lostcolony
This post seems somewhat strawman-ish to try and sell the author's language.

No one has ever argued that the reason for nulls being a problem is because
the idea of 'nothing' isn't valid; the argument has always been that they are
completely invisible to the client function; that is, if you tell me functionA
returns a string, I don't know if it -always- returns a string, or if it might
also return a null. And you as the writer of functionA might not even know, if
you're dependent on still other functions whose contracts are also ambiguous.

To that end, to fix it, a type system to make the implicit null explicit, has
always been the necessary answer. It -is- in fact what Option/Maybe/etc are
intended to do, and provided they're used in a language that has a
sufficiently robust type system, and that the language checks for non-
exhaustive patterns in case matches (or similar), are still 100% effective.

All of this author's issues with an Option type are explicitly with Java's
implementation of it, a fact he himself notes.

When he's able to show why his language provides better null protection than
Haskell or Ocaml or something, I'll be interested. But as it stands, it sounds
like he's just making an Option type that is a keyword in the language
("nullable a" rather than simply "a"), rather than a natural extension of the
type system (Maybe a = Nothing | Just a).

EDIT: This is especially galling as he's clearly aware of at least Haskell (he
mentions that Haskell has the Maybe type), but then dismisses Option/Maybe at
large for the reasons he dismisses Java, despite none of the arguments used
against Java applying.

Were the author to rewrite this, detailing the basic problem of null being
that of no contractual check for it (rather than a strawman of there being no
reason to have an 'empty' value), were to rewrite the Option/Maybe bit to show
he understands it works fine so long as the language literally does not allow
null, except as the Nothing part of the Optional/Maybe type, and instead sell
PPL as being an imperative (so no need to drink the functional koolaid),
simply typed (no ADTs, no covariant/contravariant OOness) language that has
the same no NPE guarantees as Haskell or similar (using effectively the same
mechanism; that nulls can only exist when the function contract explicitly
declares them as a possibility, and that any consumer of the function must
have a code branch to check for it and handle it in some way), it would
actually be somewhat interesting; I could see a language like that making
headway on the JVM, as it basically would come across "As simple as Java,
without the NPEs", which would be a pretty easy sell.

~~~
toolslive
Indeed. The author does not seem to appreciate (or comprehend) the difference
between types 'a' and 'a option'. This is not surprising: I have seen
architects that never did anything else than java|python struggle with the
basic types in OCaml| Haskell because they could not translate the concept of
a value into Java. They always ask "yeah, but what if it's None?" and the
answer "It can't be None as that is of a different type" does not seem to
compute.

~~~
wisty
As a Pythoneer with a vague knowledge of Haskell, that's odd.

None is not an int. Or a float. Or a string. It's None. Python is nothing like
C, where a string is a pointer that _might_ point to null ... it just can't
happen.

Variables are untyped (so you can, in theory, return an open file when the
function usually returns a float), but generally that's not how things work.

I'd think Python programmers would get more upset about when stuff gets
computed. "So ... it computes that next?". "Um, that's up to the compiler".
"The what?"

~~~
lostcolony
I think the misunderstanding goes like this -

"Okay...so you return either Maybe (value), or Nothing. But what if they
return Maybe (Nothing/null)?" "Can't happen." "What do you mean it can't
happen?" "It literally can not happen. The compiler's type checking enforces
that when a function returns Maybe A, it really is of type A, which can not be
null or nothing.".

And that's where both sides sometimes break down; Pythonistas can, yes, have a
hard time understanding that the compiler can enforce that, and Java people
can have a hard time getting past the idea of null being a possibility
everywhere.

I find it easier to explain coming from the other direction.

"When does a value get assigned null?" "Umm..." "Either you have a variable
that has never been given a value, if the language lets you, so is defaulted
to null, or you assign the variable to null." "Ok." "Now, you can only assign
the variable to null if either you do that explicitly (a = null), or, you are
calling out to another function that returns null (a = func()). But then, that
function can only return null in the same cases; either the author explicitly
assigns null, or he calls another function that returns null." "Right..." "So
eventually, to have a null bubble up, someone had to assign or return a null.
So your two cases, really, are either you left something uninitialized, or
someone assigned/returned a value of null" "Gotcha." "So what if our language
prevents you from leaving variables uninitialized, -and- there is no null
value?" "You can't have an NPE. But what if there is no valid value? Do you
just throw an exception or something?" "You -could-, but that's not ideal.
What we do instead, is we change the function signature. That's where the
Maybe comes in. Rather than just returning (type), we return Maybe (Type),
where the actual values when run are either Just (value), or Nothing. Nothing,
in this case, is the equivalent of null, but the caller of the function knows
to check for it, and the compiler can be told to MAKE them check for it. And
because we have no uninitialized values anywhere, we can't unexpectedly come
across a null, and because there are no nulls to assign, anywhere that might
be a null is itself a Maybe, and it's handled as such everywhere."

------
drfrank
As I understand it, this article misrepresents the purpose and nature of the
Null Object Pattern ("NOP").

The author cites a comment that describes (very) vaguely a "hotel booking"
example, expressing the idea that "None" is the null object. But "None" is not
an instance of the NOP. None has no interface. You can't tell None to do
something.

In contrast, an acutal implementation of the NOP might be in a travel
reservation service which allows you to reserve both a hotel and a rental car
at the same time. Somewhere in the guts of that service there might be a Trip
object.

    
    
      class Trip
      {
        Car car;
        Hotel hotel;
      
        public void Reserve()
        {
          this.car.Reserve();
          this.hotel.Reserve();
        }
      . . .
      }
    

A Trip can be constructed with a NullCar when the user doesn't want to reserve
a car on this trip. When NullCar::Reserve is invoked, it doesn't do anything,
but Trip doesn't care, and the implementor of Trip doesn't need to remember to
check its car to see if it's null before invoking it.

I've encountered this misconception frequently, and it typically stems from
the common understanding of object oriented programming as programming in
terms of entities which represent physical objects in the real world. I deduce
that the author uses this definition from the sentence "It is also interesting
to note that appearances of null objects are virtually non-existent in real
life."

Objects aren't necessarily representations of physical entities. Objects are
simply things other objects (or procedural code) can use to accomplish
something without having to know how to accomplish that thing.

I've found the distinction that Alan Kay has (repeatedly) made useful in
helping people understand this idea: Property accessors aren't for objects,
they're for Data Structures.

So, when the author says that, "the basic idea of the null object pattern is
exactly the same as for practices like 'use zero instead of null'", my reply
is, "No, it's not called the 'Null Data Structure Pattern'." Use zero instead
of null is (flawed) advice about how to write data structures, not how objects
should be constructed. The NOP is not used to build objects that represent
data, it is used to build objects that represent action. The author's repeated
use of the GetCustomer example is inappropriate for the NOP. It's much more
likely that a database record is represented using a data structure than an
object.

Pragmatically, if you feel the desire to ask an object if it is the
NullObject, you are not writing object-oriented code, you are writing
procedural code. Instead of asking if the object is the NullObject, move the
branch that would be invoked if the object were the null object into a method
present in the interface that the NullObject implements. Perhaps you find that
you don't actually have a NullObject, after all.

------
Sharlin
Yes, by all means force people to check for nulls _when null is a semantically
valid value!_ But much more importantly, in statically typed languages, allow
people to distinguish between nullable and non-nullable types to prevent the
most common null safety issue: accidentally passing a null value into a
routine whose precondition forbids null, or storing null in a variable whose
invariant forbids null.

~~~
eudox
Essentially this. Some pointers are null, some are not, and the type system
should let you express this.

------
kelseyfrancis
"There are more methods in Java's Optional class, but they are irrelevant for
the discussion in this article."

This statement represents a bad misunderstanding of the point of Optional. The
fact that is has map, flatMap, and ifPresent methods is _very_ relevant to the
discussion in this article.

------
striking
Type systems that incorporate "nullables" allow programmers to have the
concept of "nothing was returned" but force them to check it whenever they
test for data. Otherwise their programs don't compile (see the Maybe monad in
Haskell). In a serious post about null, how could such a serious point be
missed?

For example, in Haskell:

    
    
      case objectMaybe of
        Just object -> do
          (your code that deals with `object` here)
        Nothing -> do
          (your code that deals with no object)
    

Without the Nothing clause in the matching statement, this code will not
compile.

~~~
VMG
Not quite.

    
    
        sh-4.3$ cat test.hs
        main = let x = Nothing
               in case x of
                  Just x -> putStrLn "something"
        sh-4.3$ ghc test.hs
        sh-4.3$ ./test 
        test: test.hs:(2,11)-(3,40): Non-exhaustive patterns in case

~~~
dllthomas
Alternatively,

    
    
        sh-4.3~$ ghc -Wall -Werror test.hs
        [1 of 1] Compiling Main             ( test.hs, test.o )
    
        test.hs:1:1: Warning:
            Top-level binding with no type signature: main :: IO ()
    
        test.hs:3:16: Warning:
            This binding for `x' shadows the existing binding
              bound at test.hs:1:12
    
        test.hs:3:16: Warning: Defined but not used: `x'
    
        <no location info>: 
        Failing due to -Werror.

~~~
VMG
This is where it gets weird on my system

    
    
        sh-4.3$ ghc --version
        The Glorious Glasgow Haskell Compilation System, version 7.8.3
        sh-4.3$ ghc -Wall -Werror test.hs
        sh-4.3$ 
    

Arch Linux 64bit. It works as expected with _runhaskell_ though

Edit: see answer below, thanks dilthomas!

~~~
dllthomas
Try touching test.hs (or add -fforce-recomp). By default, ghc won't rebuild if
the object is newer than the source.

------
dbpatterson
How can they mention Ceylon, Eiffel, Kotlin, and PPL (the latter two I've
never heard of, and I've heard of a lot of programming languages) and not
mention Haskell, ML, OCaml, etc? It's been mentioned in other comments, but
essentially all of the supposed downsides of the Optional solution are
implementation weaknesses in either the type system (allowing null anywhere,
regardless of type) or runtime (there being overhead of Options) - which seems
like a pretty weak argument for loving null.

------
danabramov
The author noted it, but still, all listed Optional/Maybe “disadvantages”
apply to Java specifically and don't really apply to the languages where it
was available from the beginning and has some kind of syntax support.

~~~
dllthomas
It doesn't even need syntax support, if you are comfortable with higher order
functions. Java's approach of forcing the same "check, use" pattern on you
makes their Optional only a modest gain. Compare with Haskell's maybe
function:

    
    
        maybe :: b -> (a -> b) -> Maybe a -> b
    

You provide a default _result_ , a function from the value (if it's there) to
that result type, and the optional value. There is no way to use this wrong -
you can't forget to check because there is no check.

Edited to add: Apparently Java _does_ hand you the tools to do it right, if a
little ugly, using ifPresent and a purpose-built Consumer. My Java is rusty,
but I'll try and put an example together in a bit.

~~~
dllthomas
So, you can do the following:

    
    
        interface OverOptional<S,T> extends Consumer<T> {
            public S getResult();
        };
    
    

Which allows you to create an anonymous class that embodies the first two
arguments of maybe:

    
    
        Optional<Integer> maybe_int = Optional.of(7);
    
        OverOptional<Boolean, Integer> is_seven_or_empty
                = new OverOptional<Boolean, Integer>() {
            private Boolean result = true; // default
    
            public void accept(Integer i) {
                result = i == 7;
            }
    
            public Boolean getResult() { return result; }
        };
    
        maybe_int.ifPresent(is_seven_or_empty);
    
        return is_seven_or_empty.getResult();

------
zokier
Isn't the difference between compiler-enforced null checking and Option/Maybe
type (+no null) mostly superficial? Eg compare and contrast the PPL example
from the article:

    
    
        const nullable string email = system.console?.ask_string ( "Please enter Alice's email address: " )
    
        if email is not null then
           system.out.write_line ( """Alice's email address is {{email}}""" )
        else
           system.out.write_line ( "Alice doesn't have an email address." )
        end
    

to hypothetical option type:

    
    
        const option<string> email = system.console?.ask_string ( "Please enter Alice's email address: " )
    
        if email.is_present() then
           system.out.write_line ( """Alice's email address is {{email.get()}}""" )
        else
           system.out.write_line ( "Alice doesn't have an email address." )
        end

~~~
lomnakkus
Not with a good type system, i.e. a type system which supports algebraic types
(or equivalent) and which explicitly disallows null. With such a type system
you wouldn't be able to write the second example as is since string
interpolation/concatenation wouldn't be defined for option<string>, only
string. You would write something like:

    
    
      const option<string> maybeEmail = ....
    
      case maybeEmail of
          Some email -> system.out.write_line("....{{email}}")
          None -> system.out.write_line("Alice doesn't have an email address.")
    

The thing to notice here is that we're accessing "email" and not "maybeEmail"
in the Some case. This guarantees that we cannot get a null value (since the
possibility has been eliminated by the case match).

This simple example doesn't do it justice, however. The real benefit comes
when you have functions taking option<X> vs. X arguments and/or returning
option<X> vs. X. If you get a plain X (either as a return value or parameter)
you _know_ that it cannot be null. No such luck in a language which permits
"null" as a value for all (reference) values.

Of course algebraic data types go much further than just this. I'd encourage
you to look into them in e.g. Haskell or some variant of ML.

------
jicea
_Among the 20 most popular programming languages listed at the Tiobe index
(Basic, C, C++, C#, Java, JavaScript, Lisp, Objective-C, Perl, PHP, Python,
Ruby and more) none of them has null-safety built-in._

Juste some nuances for Objective-C: in Objective-C, it's perfectly valid to
send any message to nil. For instance:

    
    
      id someObject = nil;
      [someObject setVisible:YES];
    

Is a valid code. I really appreciate this language feature, and avoid you a
lot of boilerplate test. The problem is that nil as a parameter of a function
(selector in Objective-C world) can trigger exception:

    
    
      id someObject = nil;
      [myArray addObject:someObject];
    

will trigger an exception.

~~~
darylteo
My gripe with that is that fail-silent scenarios can lead to some very nasty
hard-to-find bugs. I've been bitten by it on several occasions.

Fail fast and with lots of explosions, is the way to go for me.

------
zak_mc_kracken
Nice article. I thought it was going to be superficial and rehashing common
ideas but the author covers a lot of ground and languages in this overview.

Of all the languages I've encountered with null/Option/Optional/Maybe in, the
one I think is the most interesting is Ceylon.

Ceylon supports sum types, so you can say that a type is "Account" (non
nullable) or "Account|Null" (which can be abbreviated to "Account?").

The compiler will not let you dereference a nullable type, you will have to
switch/type cast it first:

    
    
        Account? account = ...
        if (exists account) {
          // account has type Account, safe to dereference
        } else {
          // account has type Null, can't dereference
        }
    

[http://ceylon-lang.org/](http://ceylon-lang.org/)

~~~
zeckalpha
This is essentially how it is in Haskell, Scala, Swift, etc.

~~~
zak_mc_kracken
Not really. Haskell and Scala use Maybe/Option, which are monads enabling the
composition of operations, even in the absence of values. This leads to a more
powerful abstraction (composition) at the expense of heavier syntax and the
necessity of libraries that are monad aware.

Ceylon's approach is more of a middle ground with limited composability but
straightforward syntax and a much simpler type system.

~~~
wtetzner
No, in Haskell and Scala Maybe and Option are sum types. There also happen to
be monad operations defined on those types, but you don't have to use them.

------
VMG
> null is what allows us to state things like:

> Alice's birth date has not yet been entered in the database
> (alice.birth_date = null)

Better solved with Maybe (see other references in threads)

> No orders have yet been placed by this customer (customer.order_list = null)

Should be an empty list

> The operation succeeded without any errors (error = null)

The Either data type is a good solution here, because you can prevent
accidentally operating on an invalid result if there was an error

> There is no more beer in the fridge (fridge.beer = null)

Should be 0 or an empty list

------
ehartsuyker
> This is a trivial example of a null handling situation. Nevertheless, the
> only way to know what will happen is to try it out because it really depends
> on the programming language and the compiler we use. Ask any experienced
> Java programmer and you will see that most of them (me included) will
> struggle to predict the actual behaviour without hesitation.

Yes, because what I want to do is waste my time figuring out what happens when
I apply functions to null values.

------
nawitus
I think PPL's nullable semantics are the best solution to the null problems.
It makes it clear when you actually need to check for null.

------
StephenGL
These bugs are notbcauswd by null they are caused by M is understanding type
systems and null itself.

------
michaelochurch
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
_" 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
Surprisingly, 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.

