First, full credit for the term “Null Hot Potato” goes to Reid Evans. He was presenting his talk “C# Without Nulls or Exceptions” at our local Windsor-Essex .NET Developers group. I don’t want to give away the talk, because I hope it gets posted online so you can watch it yourself. But I do want to dive into this one specific thing that Reid described as of the Null Hot Potato.
Sponsor: Do you build complex software systems? See how NServiceBus makes it easier to design, build, and manage software systems that use message queues to achieve loose coupling. Get started for free.
Not My ProblemIt’s really a case of passing the responsibility. If your method handles null with null checks, but returns null, your just “passing the buck”. You’re basically saying to the caller: “I handled it properly, if you don’t that’s your problem”. Not exactly that friendly.
ExampleEasiest way to describe this through some trivial code snippet. Here we have a simple method that takes a
stringand returns a
stringwith no white-space. What happens if we pass in
nullas the parameter? Well because we are good developers, we used the null conditional operator to do our null check. Ultimately, this will return
nullto the caller. This is null hot potato. Just because one piece of code that we may or may not own does the null check, does not mean anywhere else will. Ultimately null checks will be permeated through our code. Your code, like mine, likely has null checks littered everywhere.
ExpectationsShouldn’t it be reasonable to assume that if the signature of a method states that it takes a string and returns a string, it should actually do that. Wouldn’t that just make sense? How about a method that throws an exception? As the caller of that method, should I have any expectation it might throw?
Option/MaybeThis post is really an extension about my Avoiding the NullReferenceException post. Using an option type from the Optional package, we get rid ourselves of the null hot potato. We can be good code citizens and stop the madness of passing null off to the next caller. Now if we are calling this method, there’s no surprises. We are always going to get an
6 thoughts to “Null Hot Potato”
I love the “null hot potato” term!
But I don’t think I understand the gist of what you’re trying to say. I think you’re saying that null hot potato is bad, because the return value can also be null, so you have to propagate those null checks throughout your code.
If that’s the case, then I don’t understand how Option is better. It’s certainly more explicit, but to me it seems to be doing the same exact thing. And it has the same problem – as long as your return value is in the Option monad, you’re just forcing your caller to Match into it. So Option is equivalent to “reference type”, and Match is like a null check, only a lot more awkward.
I conclude then that reference types *are* actually Options – an unfortunate conflation, but one we have to live with.
So I think I can see what you’re trying to do here in regards to type safety. I’m just not convinced it’s worth it. Of course, in an ideal world, we’d have explicitly nullable reference types, in which case Nullable would be your Option. But it’s super hard to fix a type system whose original design is so broken. Every once in a while MS tries to add “nonnullable reference” types, and they can never get it to work.
I agree.. great term, but not so great alternative.
Null hot potato is obviously bad… but I see it more of a problem of unstable codebase with developers not trusting anything beyond method which is being worked on currently & being protective against dodgy behaviours from everything around. Which quite tends to happen during rushed bug fixing.
Optional itself is not a solution, it just repackages the problem of “if” conditional to conditional delegate invocation. I think it is mostly a good vehicle to spread awareness about Null hot potato problem 🙂
To minimise potatoes I’d rather try to shield my application from externals that like to return null by putting anticorruption layer(s) responsible for conversion of nulls into something better… combined with “not returning nulls” practice within application. No silver bullet as it easy to say, but hard to execute, as most of the things 🙂
You’re on the money with what the post was. The gist being that the Option forces you to write with the match for the both Some and None parts of the equation. If a nullable and if/else, nothing is forcing you as a developer to write the IF statement or the ELSE. So yes, its not just more explicit, it’s forced.
I wish the Nullable didn’t have a .Value property but rather had a Match method. I do agree it can feel awkward.
Really you have a few options:
– Return null because your function or whatever doesn’t like/accept it (hot potato).
– Throw an exception because you really don’t like it & it indicates a problem. But basically is the same as above.
– Decide to handle it with a logical return value. But it might only be logical to you. Needs extra documentation.
– Swallow it as if it were a valid input & go along with the process. Similar to above. But might also mean silently failing.. not good.
Regardless the problem is not your handling of null, but that you were given a null. Making it your problem, just enables those erroneous calls.
I prefer to know if I gave a function something it didn’t want.
To your last statement, you would prefer to know at runtime with an exception? To each there own but i’d rather not deal with exceptions and try/catch. Hence why things like TryParse exist.
Related to the input being the problem, then my immediate answer would be to not accept anything that can be null, then the caller can’t send it. Meaning, if you only accepted an Option in my example, rather then a string.
I do like the OptionOfT approach, but it only becomes obvious when you start composing and use the Map/FlatMap methods.. (same goes for the Either monad)..