There's plenty of excellent answers that cover the unfortunate symptoms of `null`, so I'd like to present an alternative argument: **Null is a flaw in the type system.**

The purpose of a type system is to ensure that the different components of a program "fit together" properly; a well-typed program can't "off the rails" into undefined behavior.

Consider a hypothetical dialect of Java, or whatever your preferred statically-typed language is, where you can assign the string `"Hello, world!"` to any variable of any type:

    Foo foo1 = new Foo();  // Legal
    Foo foo2 = "Hello, world!"; // Also legal
    Foo foo3 = "Bonjour!"; // Not legal - only "Hello, world!" is allowed

And you can check variables like so:
    
    if (foo1 != "Hello, world!") {
        bar(foo1);
    } else {
        baz();
    }
There's nothing impossible about this - someone could design such a language if they wanted to. The special value need not be `"Hello, world!"` - it could've been the number 42, the tuple `(1, 4, 9)`, or, say, `null`. But why would you do this? A variable of type `Foo` should only hold `Foo`s - that's the whole point of the type system! `null` is not a `Foo` any more than `"Hello, world!"` is. Worse, `null` is not a value of *any* type, and there's nothing you can do with it!

The programmer can never be sure that a variable actually holds a `Foo`, and neither can the program; in order to avoid undefined behavior, it has to check variables for `"Hello, world!"` before using them as `Foo`s. Note that doing the string check in the previous snippet doesn't propagate the fact that foo1 is really a `Foo` - `bar` will likely have its own check as well, just to be safe.

Compare that to using a `Maybe`/`Option` type with pattern matching:
    
    case maybeFoo of
     |  Just foo => bar(foo)
     |  Nothing => baz()

Inside the `Just foo` clause, both you and the program know for sure that our `Maybe Foo` variable truly does contain a `Foo` value - that information is propagated down the call chain, and `bar` doesn't need to do any checks. Because `Maybe Foo` is a distinct type from `Foo`, you're forced to handle the possibility that it could contain `Nothing`, so you can never by blindsided by a `NullPointerException`. You can reason about your program much more easily and the compiler can omit null checks knowing that all variables of type `Foo` really do contain `Foo`s. Everyone wins.