Not Null — How to Avoid Null Errors in your Code

Alternative to null/undefined

Varun Pujari
Level Up Coding

--

Photo by Nate Grant on Unsplash

The most common error/exception I have seen regardless of which language or framework I used is NullPointer error or Undefined error. Different names, but what it simply means is you are trying to access something which does not exist.

To better handle these errors, languages started adding support for null safety. Now, what is null safety? Null safety means if a variable you are using is going to be null, you have to explicitly indicate it. In many languages, it indicates using ?.

Let’s look at an example

For example, let’s assume the following class.

class Dog {}

Now, to an example. Before null safety

Dog dog = new Dog();

Now, this dog variable definition can either be an instance of Dog or null. But notice it doesn’t say anywhere that it can be also null. Meaning, it is up to the variable consumer to make sure the dog is not null before using it.

Now imagine, in a big project, there are lots of variables. And every variable has a possibility of being null.

Thinking that we have been writing code for so long. Without indicating that something might not exists. It is actually surprising.

Welcome null safety

Here comes the null safety. Meaning, if a variable is going to be null you can indicate that using ?.

Example

Dog? dog = new Dog();

dog variable just cannot be a Dog. There is a possibility of it being null as well. That is what the ? indicates. It is a nullable Dog so to say.

The benefit of this is that you cannot directly use the dog variable anymore. You have to check for nullability or use null operator.

Example

Let’s add an eat method on Dog class.

class Dog {
void eat() {}
}

Say you want to call eat method on dog. Earlier you had to do dog.eat();. Now the dog is nullable. You have to dog?.eat();. The difference is that if the dog is null. This program will not fail and continue to run. Compared to the earlier case dog.eat();. If the dog is null. It will fail.

Compilers are also smart enough to help you avoid double-checking for nullability.

Example

if (dog != null) {
dog.eat(); // no ? operator
}

Even though the dog is nullable here. The compiler will not complain because it saw that we perform a null check before using. Please note, this will only work when the dog variable is defined in local scope.

Let’s add food arguments to dog’s eat method.

class DogFood {}class Dog {
void eat(DogFood food) {}
}

The dog cannot eat null. It needs food. Means, the food we pass to dog cannot be null.

Dog? dog;
DogFood? food;
dog?.eat(food); // fails

It will fail because the food can be null. We need to do some null checking to make it work.

if (food != null) {
dog?.eat(food);
}

Or

if (dog != null && food != null) {
dog.eat(food);
}

You can notice that the code quickly starts looking ugly. Imagine having a few more nullable variables to use. You have to sprinkle your code with if conditions everywhere.

Null safety is good. We can do better.

Welcome Option

The Option has different names in different programming languages. Like Optional in Java, Maybe in Haskell, Option in dart’s fpdart library and Option in typescript’s fp-ts library. I’m going to use the fpdart library’s Option.

Option is like a wrapper for your variable. Indicating if a value is present or not. But It is not just that. If it was just that. There is no much difference between using null safety and Option. The benefit is that we can write our code in a more readable and understandable manner without cluttering it.

Before we dive into what we can do with Option. Let’s try to understand Option.

Option comes from FP world. But I’m not going to using the FP term monad or monadic to scare you away. Instead we will look at it from a practical perspective.

Think of Option like a List. A list is a container of multiple values. In the same way Option is a container for only 1 value or nothing.

If there is a value present. The Option will be an instance of Some. Otherwise it will be an instance of None. We can dig deeper into how Option is implemented. What are all the laws it has to adhere. But I will keep it simple.

Example

Like we create a List of number

List<int> numbers;

We can create a Option of number

Option<int> number;

We are calling it a number. It is actually an Option of number. Maybe we can call it optNumber or maybeNumber. But again keeping things simple. Let’s go with number.

You can initialise the number as below.

number = None(); // or Option.none();

This indicates that the option is empty and does not contain a value.

number = Some(10); // or Option.of(10);

This indicates that there is some value in the Option. In this case it is 10.

Now, we know the basics of what is Option. Let’s try to use it for our dog and food example.

Option<Dog> dog = Option.of(new Dog());
Option<DogFood> food = Option.of(new DogFood());

To feed the food to dog. We can do below without using if conditions.

dog.flatMap((d) {
return food.map((f) => d.eat(f));
});

If this is your first time using flatMap and map or basic functional programming. You might not understand what going on here at all.

But let me explain.

First, let’s look at map. It is very similar to doing array.map. Where every item in list get mapped to something else. But in case of Option. Only 1 item gets mapped to something if it exists.

Coming to flapMap. One of the best way of understanding what flapMap is by looking at the method signature differences of map and flapMap.

FlapMap signature

Option<B> flatMap<B>(Option<B> Function(T t) f)

Map signature

Option<B> map<B>(B Function(T t) f);

If you look at the difference. The only thing difference you will find is the return type of f function argument.

In case of flatMap. It is returning Option<B>. And in case of map. It is returning only B.

Simply put. flatMap allows you to consume the value and produce a new value wrapped in Option.

But in case of map. It allows you to consume and produce a new value.

In our code above. We are unwrapping the values from Option and consuming. If any of the values (dog or food) is not present the function flatMap or map will not be called at all.

It is very similar to how an array.map works. If you call array.map on an empty array. There are no items in array to map. So, the map function will never be called.

Another Example

Let’s look at another example. To understand how powerful Option is.

For our example. Let’s assume we need to get user’s address. There are 3 functions by which you can get the user’s address.

Option<String> getAddressFromDevice() {
...
}

Option<String> getAddressFromRemote() {
...
}

Option<String> getAddressFromThirdParty() {
...
}

For simplicity purpose. I’m not going to implement the functions. But please notice. How 3 functions return Option<String>. Indicating the function might not return any address at all.

To get user address we should try the functions in following order.

  • getAddressFromDevice
  • getAddressFromRemote
  • getAddressFromThirdParty

If none of these function return an address. We can fallback to Hyderabad, India as default address.

Putting things together. This is how the code looks.

final address = getAddressFromDevice()
.alt(() => getAddressFromRemote())
.alt(() => getAddressFromThirdParty())
.getOrElse(() => "Hyderabad, India");

Let me explain the alt function. The alt stands for alternative. It is used to provide an alternative Option in case the current one is None.

So, what we are doing here is. First we try the getAddressFromDevice, then getAddressFromRemote, then getAddressFromThirdParty. If none these return any address we will fallback to value return from getOrElse.

Also notice, if any of the function succeeds to get the address. We will not call the next functions. For example, let’s say the getAddressFromDevice succeeded. It will not call getAddressFromRemote and getAddressFromThirdParty at all.

To understand how complicated this can be. Try writing the above code without using Option. You will release how powerful the Option is.

This is just scratching the surface. There is a hell lot more to learn.

Please let me know if you want more articles like this in the comments. I will try to cover more topics like this.

I hope you liked the article and learnt something new. Please give it a clap and follow me for more.

Thank you for reading.

--

--