loading...

Stop returning Null!

lysofdev profile image Esteban Hernández ・2 min read

We've all been there. Tireless hours of testing and careful development halted by a mysterious NullPointerException or similar problem all due to some undefined value. What are we to do?

Whoever came up with null, None, nil, etc. really hated humanity. I get it. We need a value that represents none-existence but isn't there a better way? Let's try skipping null altogether will lists. I'm going to show some code examples in Javascript since most people here understand it and it's a particularly stupid language given that it somehow justifies having two none-existing values null and undefined. Ah, yes, undefined. The Schroedinger's cat of the Web.

We might encounter the following in the wild:

let counter;

...some code that may or may not define counter...

if ( counter !== undefined && counter !== null ) {
    counter++;
}

What a mess, amirite? Some might even say we should be using typeof to be extra careful but we can remove this whole problem with lists. Here's a basic attempt.

const counters = [];

...some code that may or may not define a counter inside counters...

if ( counters.length >== 1 ) {
    counters[0]++;
}

Now our if statement is a bit simpler but we still have to check before actually performing the operation. Wouldn't it be great if we could just perform the operation without checking for null or if the containing list is empty? Well, let's see what our good, old friend forEach can do for us.

const counters = [];

...some code that may or may not define a counter inside counters...

counters.forEach( counter -> counter++ );

And, boom! This operation will never fail! If counters is empty then the operation will run 0 times. If counters has 1 counter in it, the operation will run 1 time. Finally, if counters has n counters in it, the operation will run n times.

Amazing! We don't need to check for undefined values anymore. We can just reliably define container lists and run list operations on those lists which guarantees that operations will not throw exceptions when no values are defined.

Posted on Oct 2 '18 by:

lysofdev profile

Esteban Hernández

@lysofdev

Software Engineer specializing on performant web applications.

Discussion

markdown guide
 

That's actually an interesting solution. Abstracting that array to just a generic wrapper let's say (like the Nullable in C# or Optional in Java) it could work.

A few questions though:

  • What do you do with arrays? Do you just .forEach(...).forEach(...)? Isn't that really difficult to read?
  • What about properties on objects? let counter = {} and have to check for counter.elements to be instantiated.

My rule of thumb here is to never use null and simply check for undefined as my sole "none" value. So that if statement would be reduced to:

if (counter !== undefined) { ... }

And, if I know that counter has to be an object then I can simply do

if (counter) { ... }

Most JS programmers would recognize this pattern.

 

I'm definitely for Generic Wrappers and these can be easily implemented with the above list strategies.

As for forEach(...).forEach(...), that will actually fail since forEach(...) is a consumer function; it doesn't return anything. Instead use map() which is the same as forEach() but it returns a value. I personally like to do stack method chains like so:

items
    .map(...)
    .filter(...)
    .map(...)
    .forEach(...);

That style blends well with list method chains, Promise chains and Streams.

I like the if (counter) style but this isn't going to work in every language and my employer doesn't allow it in our JS code. We have to always use the double check unfortunately.