Getting Rid of Nulls Is Indeed A Good Idea
14 Nov 2024 #csharpThese days I found a Medium article titled: Why Eliminating NULLs From Your Code Will Not Make Your App Better.
Its point is that when we stop using null, we replace checking for null with checking for a default value or a wrapper like Result. And there’s no major gain.
But there is.
The advantage of returning a wrapper like Option or Result instead of null is making the implicit explicit.
Instead of getting a NullReferenceException and saying “ooops, that method returned null,” we look at a method signature and say “Hey, that method might return null. We’re better off checking for that.”
That’s Method Signature Honesty. When the parameters and return types of a method show what it does, even for edge cases.
For example, what does this method do?
Movie GetMovieById(int movieId);
What if there was no movie found? What if there was more than one? Does the method throw an exception? Returns null? Returns a Movie.Empty? We can’t tell just by looking at the signature.
But what about?
Option<Movie> GetMovieById(int movieId);
// or
Result<Movie, MovieError> GetMovieById(int movieId);
At least, it’s obvious from that signature that there’s a special case we need to handle. It’s up to the caller to decide what to do with it.
The original article has a point that replacing a null checking like,
var movie = GetMovieById(someId);
if (movie != null)
{
// Do something here
}
With,
var result = GetMovieById(someId)
.Map(movie => /* Do something here */);
// And at some point later:
result.OrElse(() => /* Nothing found. Do something else here */);
is a design choice. For C#, the first one looks like more “native.”
In either case, at some point, we have to convert the absence of data (either a null or a None) to something else like a status code or error message.
C# didn’t take the wrapper route and introduced nullable references instead.
With nullable references turned on, all our object references are not null by default. If we want a reference to accept null, we should annotate the type with a ?. The same way we do it for nullable ints.
Movie? GetMovieById(int movieId);
// ^^^
// It might be null.
Yes, that name is kind of misleading. It should be “not nullable” references.
That’s a feature we should turn on and make all nullable warnings as errors.
With Option, Result, or nullable references, we make our method signatures honest. That’s already a gain.