26

This is one of the rules that are beaing repeated over and over and that perplex me.

Nulls are evil and should be avoided whenever possible.

But, but - from my naivety, let me scream - sometimes a value CAN be meaningfully absent!

Please let me ask this on an example that comes from this anti-pattern ridden horrible code I'm working on at the moment. This is, at its core, a multiplayer web turn based game, where both players' turns are run simultaneously (like in Pokemon, and oppositely to Chess).

After each turn the server broadcasts a list of updates to client side JS code. The Update class:

public class GameUpdate
{
    public Player player;
    // other stuff
}

This class is serialized to JSON and sent to connected players.

Most updates naturally have a Player associated with them - after all, it is necessary to know which player made which move this turn. However, some updates can't have a Player meaningfully associated with them. Example: The game has been force tied because the turn limit without actions have been exceeded. For such updates, I think, it is meaningful to have the player nulled.

Of course, I could "fix" this code by utilizing inheritance:

public class GameUpdate
{
    // stuff
}

public class GamePlayerUpdate : GameUpdate
{
    public Player player;
    // other stuff
}

However, I fail to see how this is of any improvement, for two reasons:

  • The JS code will now simply receive an object without Player as a defined property, which is the same as if it was null since both cases require checking if the value is present or absent;
  • Should another nullable field be added to the GameUpdate class, I would have to be able to use multiple inheritance to continue with this desing - but MI is evil in its own right (according to more experienced programmers) and more importantly, C# doesn't have it so I can't use it.

I have an inkling that this piece of this code is one of the very many where an experienced, good programmer would scream in horror. At the same time I can't see how, in this place, is this null hurting anything and what should be done instead.

Could you explain this issue to me?

  • 2
    Is this question specific to JavaScript? Many languages have an Optional type for this purpose but I don't know if js has one (Although you could make one) or if it would work with json (Although that's one part of nasty null checks rather the all over your code – Richard Tingle Jul 7 '18 at 21:56
  • 9
    That rule is just false. I'm a bit astonished that someone would subscribe to that notion. There are some good alternatives to null in some cases. null is really good for representing a missing value. Don't let random internet rules like that prevent you from trusting your own reasoned judgement. – usr Jul 9 '18 at 21:31
  • 1
    @usr - I totally agree. Null is your friend. IMHO, there's no clearer way to represent a missing value. It can help you find bugs, it can help you handle error conditions, it can help you beg for forgiveness (try/catch). What's not to like?! – Scuba Steve Feb 26 at 23:33
  • 4
    one of the first rules of designing software is "Always check for null". Why this is even an issue is mind boggling to me. – MattE Feb 27 at 21:46
  • 1
    @MattE - Absolutely. Why would I engineer a design pattern when really, Null is a good thing to have in your basic toolbox. What Graham was suggesting, IMHO, strikes me as over-engineering. – Scuba Steve Feb 27 at 22:59
20

Lots of things are better to return than null.

  • An empty string ("")
  • An empty collection
  • An "optional" or "maybe" monad
  • A function that quietly does nothing
  • An object full of methods that quietly do nothing
  • A meaningful value that rejects the incorrect premise of the question
    (which is what made you consider null in the first place)

The trick is realizing when to do this. Null is really good at blowing up in your face but only when you dot off it without checking for it. It's not good at explaining why.

When you don't need to blow up, and you don't want to check for null, use one of these alternatives. It may seem weird to return a collection if it only ever has 0 or 1 elements but it's really good at letting you silently deal with missing values.

Monads sound fancy but here all they're doing is letting you make it obvious that the valid sizes for the collection are 0 and 1. If you have them, consider them.

Any of these do a better job of making your intent clear than null does. That's the most important difference.

It may help to understand why you are returning at all rather than simply throwing an exception. Exceptions are essentially a way to reject an assumption (they are not the only way). When you ask for the point where two lines intersect you are assuming they intersect. They might be parallel. Should you throw a "parallel lines" exception? Well you could but now you have to handle that exception elsewhere or let it halt the system.

If you'd rather stay where you are you can bake the rejection of that assumption into a kind of value that can express either results or rejections. It's not that weird. We do it in algebra all the time. The trick is to make that value meaningful so we can understand what happened.

If the returned value can express results and rejections it needs to be sent to code that can handle both. Polymorphism is really powerful to use here. Rather than simply trading null checks for isPresent() checks you can write code that behaves well in either case. Either by replacing empty values with default values or by silently doing nothing.

The problem with null is it can mean far too many things. So it just ends up destroying information.


In my favorite null thought experiment I ask you to imagine a complex Rube Goldbergian machine that signals encoded messages using colored light by picking up colored light bulbs off a conveyor belt, screwing them into a socket, and powering them up. The signal is controlled by the different colors of the bulbs that are placed on the conveyor belt.

You've been asked to make this hideously expensive thing compliant with RFC3.14159 which states that there should be a marker between messages. The marker should be no light at all. It should last for exactly one pulse. The same amount of time that a single colored light normally shines.

You can't just leave spaces between the messages because the contraption sets off alarms and halts if it can't find a bulb to put in the socket.

Everyone familiar with this project shudders at the though of touching the machinery or it's control code. It is not easy to change. What do you do? Well you can dive in and start breaking things. Maybe update this things hideous design. Yeah you could make it so much better. Or you could talk to the janitor and start collecting burned out bulbs.

That's the polymorphic solution. The system doesn't even need to know anything has changed.


It's really nice if you can pull that off. By encoding a meaningful rejection of an assumption into a value you don't have to force the question to change.

How many apples will you owe me if I give you 1 apple? -1. Because I owed you 2 apples from yesterday. Here the assumption of the question, "you will owe me", is rejected with a negative number. Even though the question was wrong, meaningful info is encoded in this answer. By rejecting it in a meaningful way the question isn't forced to change.

However, sometimes changing the question actually is the way to go. If the assumption can be made more reliable, while still useful, consider doing that.

Your problem is that, normally, updates come from players. But you've discovered a need for an update that doesn't come from player 1 or player 2. You need an update that says time has expired. Neither player is saying this so what should you do? Leaving player null seems so tempting. But it destroys information. You're trying to encode knowledge in a black hole.

The player field doesn't tell you who just moved. You think it does but this problem proves that's a lie. The player field tells you where the update came from. Your time expired update is coming from the game clock. My recommendation is give the clock the credit it deserves and change the name of the player field to something like source.

This way if the server is shutting down and has to suspend the game it can send out an update that reports what's happening and lists itself as the source of the update.

Does this mean that you should never just leave something out? No. But you need to consider how well nothing can be assumed to really mean a single well known good default value. Nothing is really easy to over use. So be careful when you use it. There is a school of thought that embraces favoring nothing. It's called convention over configuration.

I like that myself but only when the convention is clearly communicated in some way. It should not be a way to haze the newbies. But even if you're using that, null is still not the best way to do it. Null is a dangerous nothing. Null pretends to be something you can use right up until you use it, then it blows a hole in the universe (breaks your semantic model). Unless blowing a hole in the universe is the behavior you need why are you messing with this? Use something else.

  • 9
    "return a collection if it only ever has 0 or 1 elements" - I am sorry but I don't think I get it :( From my POV doing this (a) adds unnecessary invalid states to the class (b) requires documenting that the collection must have at most 1 element (c) doesn't seem to solve the problem anyway, since cheking if the collection contains its only element before accesing it is necessary anyway, or else an exception will be thrown? – gaazkam Jul 7 '18 at 22:03
  • 2
    @gaazkam see? I told you it bugs people. Yet it's exactly what you've been doing every time you use the empty string. – candied_orange Jul 7 '18 at 22:05
  • 16
    What happens when there's a valid value that happens to be an empty string or set? – Blrfl Jul 7 '18 at 22:08
  • 5
    Anyway @gaazkam, you don’t explicitly check to see if the collection has items. Looping (or mapping) over an empty list is a safe action that has no effect. – RubberDuck Jul 7 '18 at 23:29
  • 3
    I know you're answering the question about what should someone should do instead of returning null but I really do disagree with lots of these points. However as we're not language specific and there's a reason to break every rule I refuse to downvote – Liath Jul 9 '18 at 13:55
15

null is not really the problem here. The problem is how most current programming languages deal with missing information; they do not take this problem seriously (or at least dont provide the support and safety most earthlings need to avoid the pitfalls). This leads to all the problems we've learned to fear (Javas NullPointerException, Segfaults, ...).

Lets see how to deal with that issue in a better way.

Others suggested the Null-Object Pattern. While i think it is applicable sometimes, there are a lot of scenarios where there is just no one-size-fits-all default value. In those cases, consumers of the possibly-absent information have to be able to decide for themselves what to do if that information is missing.

There are programming languages that provide safety against null pointers (so called void safety) at compile time. To give some modern examples: Kotlin, Rust, Swift. In those languages, the problem of missing information has been taken seriously and using null there is totally painless - the compiler will stop you from dereferencing nullpointers.
In Java, efforts have been made to establish the @Nullable/@NotNull annotations together with compiler+IDE plugins to reach the same effect. It was not really successful but thats out of scope for this answer.

An explicit, generic type that denotes possible absence is another way to deal with it. E.g. in Java there is java.util.Optional. It can work:
On a project or company level, you can establish rules/guidelines

  • use only high-quality 3rd party libraries
  • always use java.util.Optional instead of null

Your languages community / ecosystem may have come up with other ways of adressing this problem better than the compiler/interpreter does.

  • Could you kindly elaborate, how are Java Optionals better than nulls? As I read the documentation, trying to get a value from the optional produces an exception if the value is absent; so it would seem we're at the same place: a check is necessary and if we forget one, we're screwed? – gaazkam Jul 7 '18 at 22:49
  • 3
    @gaazkam You're right, it does not provide compile-time saftey. Also, the Optional itself could be null, another issue. But it is explicit (compared to variables just possibly being null / doc comments). This gives only a real benefit if, for the entire codebase, a coding rule is set up. You may not set things null. If information may be absent, use Optional. That forces explicitness. The usual null-checks then become calls to Optional.isPresent – marstato Jul 7 '18 at 22:53
  • 8
    @marstato replacing null checks with isPresent() is not the recommended use of Optional – candied_orange Jul 7 '18 at 23:03
10

I do not see any merit in the statement that null is bad. I checked the discussions about it and they only make me impatient and irritated.

Null can be used wrong, as a "magic value", when it actually means something. But you would have to do some pretty twisted programming to get there.

Null should mean "not there", that is not created (yet) or (already) gone or not provided if it is an argument. It is not a state, the whole thing is missing. In an OO setting where things are created and destroyed all the time that is a valid, meaningful condition different from any state.

With null you get smacked at runtime where you get smacked at compile time with some sort of type-safe null alternative.

Not entirely true, I can get a warning for not checking for null before using the variable. And it is not the same. I may not want to spare the resources of an object that has no purpose. And more importantly, I will have to check for the alternative just the same in order to get desired behavior. If I forget to do that I would rather get a NullReferenceException at runtime than some undefined/unintended behavior.

There is one issue to be aware of. In a multi-threaded context you would have to safeguard against race conditions by assigning to a local variable first before performing the check for null.

The problem with null is it can mean far to many things. So it just ends up destroying information.

No, it does/should not mean anything so there is nothing to destroy. The name and type of the variable/argument will always tell what is missing, that is all there is to know.

Edit:

I am halfway the video candied_orange linked to in one of his other answers about functional programming and it is very interesting. I can see the usefulness of it in certain scenarios. The functional approach that I have seen so far, with pieces of code flying around, typically makes me cringe when used in an OO setting. But I guess it has its place. Somewhere. And I guess there will be languages that do a good job hiding what I perceive to be a confusing mess whenever I encounter it in C#, languages that provide a clean and workable model instead.

If that functional model is your mind set/framework, there is really no alternative to pushing any mishaps forward as documented error descriptions and ultimately let the caller deal with the accumulated garbage that was dragged along all the way back to the point of initiation. Apparently this is just how functional programming works. Call it a limitation, call it a new paradigm. You may regard it to be a hack for the functional disciples that enables them to continue on the unholy path a bit further, you may be delighted by this new way of programming that unleashes exciting new possibilities. Whatever, it seems pointless to me to step into that world, point back to the traditional OO way of doing things (yes, we can throw exceptions, sorry), pick some concept from it, pull it out of context and scream blue murder because it does not fit your recently discovered new world.

Any concept can be used the wrong way. From where I stand, the presented "solutions" are no alternatives for an appropriate use of null. They are concepts from another world.

  • 9
    Null destroys knowledge of what kind of nothing it represents. There's the kind of nothing that should be quietly ignored. The kind of nothing that needs to halt the system to avoid an invalid state. The kind of nothing that means the programmer screwed up and needs to fix the code. The kind of nothing that means the user asked for something that doesn't exist and needs to be politely told. Sorry no. All of these have been encoded as null. Because of that if you encode any of these as null then you shouldn't be surprised if no one knows what you mean. You're forcing us to guess from context. – candied_orange Jul 8 '18 at 14:57
  • 3
    @candied_orange You can make that exact same argument about any optional-type: Does None represent …. – Deduplicator Jul 8 '18 at 18:24
  • 3
    @candied_orange I still do not get what you mean. Different kinds of nothing? If you use null when you should have used an enum perhaps? There is only one kind of nothing. The reason for something to be nothing may vary but that does not change the nothingness nor the appropriate response to something being nothing. If you expect a value in a store that is not there and that is noteworthy, you throw or log at the moment you query and get nothing, you do not pass null as an argument to a method and then in that method try to figure out why you got null. Null would not be the mistake. – Martin Maat Jul 8 '18 at 19:03
  • 2
    Null is the mistake because you had a chance to encode the meaning of the nothing. Your understanding of the nothing does not demand immediate handling of the nothing. 0, null, NaN, empty string, empty set, void, null object, not applicable, not implemented exception, nothing comes in many many many forms. You don't have to forget what you know now just because you don't want to deal with what you know now. If I ask you to take the square root of -1 you can either throw an exception to let me know that's impossible and forget everything or invent imaginary numbers and preserve what you know. – candied_orange Jul 8 '18 at 19:12
  • 1
    @MartinMaat you make it sound like a function must throw an exception every time your expectations are violated. This is simply not true. There are often corner cases to deal with. If you really can't see that read this – candied_orange Jul 8 '18 at 23:41
7

Your question.

But, but - from my naivety, let me scream - sometimes a value CAN be meaningfully absent!

Fully on board with you there. However, there are some caveats I'll get into in a second. But first:

Nulls are evil and should be avoided whenever possible.

I think this is a well-intentioned but overgeneralized statement.

Nulls are not evil. They are not an antipattern. They are not something that is to be avoided at all costs. However, nulls are prone to error (from failing to check for null).

But I don't understand how failing to check for null is somehow proof that null is inherently wrong to use.

If null is evil, then so are array indexes and C++ pointers. They're not. They're easy to shoot yourself in the foot with, but they are not supposed to be avoided at all costs.

For the rest of this answer, I am going to adapt the intention of "nulls are evil" to a more nuanced "nulls should be handled responsibly".


Your solution.

While I agree with your opinion on the use of null as a global rule; I don't agree with your use of null in this particular case.

To summarize the below feedback: you're misunderstanding the purpose of inheritance and polymorphism. While your argument to use null has validity, your further "proof" of why the other way is bad is built on misuse of inheritance, rendering your points somewhat irrelevant to the null problem.

Most updates naturally have a Player associated with them - after all, it is necessary to know which player made which move this turn. However, some updates can't have a Player meaningfully associated with them.

If these updates are meaningfully different (which they are), then you need two separate update types.

Consider messages, for example. You have error messages and IM chat messages. But while they may contain very similar data (a string message and a sender), they do not behave the same way. They should be used separately, as different classes.

What you're doing now is effectively differentiating between two types of update based on the null-ness of the Player property. That is equivalent to deciding between an IM message and an error message based on the existence of an error code. It works, but it's not the best approach.

  • The JS code will now simply receive an object without Player as a defined property, which is the same as if it was null since both cases require checking if the value is present or absent;

Your argument relies on misues of polymorphism. If you have a method which returns a GameUpdate object, it generally shouldn't ever care to differentiate between GameUpdate, PlayerGameUpdate or NewlyDevelopedGameUpdate.

A base class needs to have a fully working contract, i.e. its properties are defined on the base class and not on the derived class (that being said, the value of these base properties can of course be overridden in the derived classes).

This renders your argument moot. In good practice, you should never care that your GameUpdate object does or doesn't have a player. If you're trying to access the Player property of a GameUpdate, then you're doing something illogical.

Typed languages such as C# wouldn't even allow you to try and access the Player property of a GameUpdate because GameUpdate simply doesn't have the property. Javascript is considerably more lax in its approach (and it doesn't require precompilation) so it doesn't bother to correct you and has it blow up at runtime instead.

  • Should another nullable field be added to the GameUpdate class, I would have to be able to use multiple inheritance to continue with this desing - but MI is evil in its own right (according to more experienced programmers) and more importantly, C# doesn't have it so I can't use it.

You shouldn't be creating classes based on the addition of nullable properties. You should be creating classes based on functionally unique types of game updates.


How to avoid the issues with null in general?

I do think it's relevant to elaborate on how you can meaningfully express the absence of a value. This is a collection of solutions (or bad approaches) in no particular order.

1. Use null anyway.

This is the easiest approach. However, you are signing yourself up for a ton of null checks and (when failing to do so) troubleshooting null reference exceptions.

2. Use a non-null meaningless value

A simple example here is indexOf():

"abcde".indexOf("b"); // 1
"abcde".indexOf("f"); // -1

Instead of null, you get -1. On a technical level, this is a valid integer value. However, a negative value is a nonsensical answer as indexes are expected to be positive integers.

Logically, this can only be used for cases where the return type allows for more possible values than can be meaningfully returned (indexOf = positive integers; int = negative and positive integers)

For reference types, you can still apply this principle. For example, if you have an entity with an integer ID, you could return a dummy object with its ID set to -1, as opposed to null.
You simply have to define a "dummy state" by which you can measure if an object is actually usable or not.

Note that you shouldn't rely on predetermined string values if you do not control the string value. You might consider setting a user's name to "null" when you want to return an empty object, but you'd encounter issues when Christopher Null signs up for your service.

3. Throw an exception instead.

No. Just no. Exceptions should not be handled for logical flow.

That being said, there are some cases where you don't want to hide the (nullreference) exception, but rather show an appropriate exception. For example:

var existingPerson = personRepository.GetById(123);

This can throw a PersonNotFoundException (or similar) when there is no person with ID 123.

Somewhat obviously, this is only acceptable for cases where not finding an item makes it impossible to do any further processing. If not finding an item is possible and the application can continue, then you should NEVER throw an exception. I cannot stress this enough.

5

The point of why nulls are bad is not that missing value is encoded as null, but that any reference value can be null. If you have C#, you are probably lost anyway. I don't see a point ro use some Optional there because a reference type is already optional. But for Java there is @Notnull annotations, which you seem to be able to set up at package level, then for values which can be missed you could use annotation @Nullable.

  • Yes! The problem is a nonsensical default. – Deduplicator Jul 8 '18 at 9:04
  • I disagree. Programming in a style that avoids null leads to fewer reference variables being null, leading to fewer reference variables that shouldn't be null being null. It's not 100% but it's maybe 90%, which is worth it IMO. – Solomonoff's Secret Jul 9 '18 at 19:24
1

Nulls are not evil, there is realistically no way to implement any of the alternatives without at some point dealing with something that looks like a null.

Nulls are however dangerous. Just like any other low level construct if you get it wrong there's nothing below to catch you.

I think there are a lot of valid arguments against null:

  • That if you are already in a high level domain, there are safer patterns than using the low-level model.

  • Unless you need the advantages of a low-level system and are prepared to be suitable cautious then maybe you shouldn't be using a low-level language.

  • etc...

These are all valid and further not having to make the effort of writing getters and setters etc is seen as a common reason not to have followed that advice. It's not really surprising a lot of programmers have come to the conclusion that nulls are dangerous and lazy (a bad combination!), but like so many other bits of "universal" advice they are not as universal as they are worded.

The good people who wrote the language thought they would be useful for someone. If you understand the objections and still think they are right for you, none else will know any better.

  • The thing is, "universal advice" is usually not worded as "universally" as people claim. If you find the original source of such advice, they usually speak of the caveats that experienced programmers know about. What happens is that, when someone else reads the advice, they tend to ignore those caveats and remember only the "universal advice" part, so when they relate that advice to others, it comes out a lot more "universal" than the originator intended. – Nicol Bolas Mar 1 at 14:51
  • 1
    @NicolBolas, you are right, but the original source is not the advice most people are quoted, it's the relayed message. The point I wished to make was merely that a lot of programmers have been told 'never do X, X is evil/bad/whatever' and, exactly as you say, it comes missing a lot of caveats. Maybe they were dropped, maybe they were never present, the end result is the same. – drjpizzle Mar 1 at 18:04
0

Java has an Optional class with an explicit ifPresent + lambda to access any value safely. This can be made in JS too:

See this StackOverflow question.

By the way: many a purist will find this usage dubious, but I do not think so. Optional features do exist.

  • Can't a reference to a java.lang.Optional be null too? – Deduplicator Jul 16 '18 at 20:53
  • @Deduplicator No, Optional.of(null) would throw an exception, Optional.ofNullable(null) would give Optional.empty() - the not-there value. – Joop Eggen Jul 17 '18 at 7:56
  • And Optional<Type> a = null? – Deduplicator Jul 17 '18 at 10:21
  • @Deduplicator true, but there you should use Optional<Optional<Type>> a = Optional.empty(); ;) - In other words in JS (and other languages) such things will not be feasible. Scala with default values would do. Java maybe with an enum. JS I doubt: Optional<Type> a = Optional.empty();. – Joop Eggen Jul 17 '18 at 11:19
  • So, we have moved from one indirection and potential null or empty, to two plus some extra methods on the interposed object. That's quite an achievement. – Deduplicator Jul 17 '18 at 11:35
0

C.A.R. Hoare called nulls his "billion dollar mistake". However, it's important to note that he thought the solution wasn't "no nulls anywhere" but instead "non-nullable pointers as the default and nulls only where they're semantically required".

The problem with null in the languages that repeat his mistake is that you can't say that a function expects non-nullable data; it's fundamentally inexpressible in the language. That forces functions to include a lot of useless extraneous null-handling boilerplate, and forgetting a null check is a common source of bugs.

To hack around that fundamental lack of expressiveness, some communities (e.g. the Scala community) don't use null. In Scala, if you want a nullable value of type T, you take an argument of type Option[T], and if you want a non-nullable value you just take a T. If someone passes a null into your code, your code will blow up. However, it's considered that the calling code is the buggy code. Option is a pretty nice replacement for null, though, because common null-handling patterns are extracted into library functions like .map(f) or .getOrElse(someDefault).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.