Safe navigation in languages


As we saw in the previous post, Ruby development team introduced a safe navigation operator to get rid of painful chaining of objects, which could raise NPE in case of null object in the middle. But Ruby is not the only language that came up with this idea. Most of the languages have introduced this operator, usually called null propagation, or some other approaches to achieve the same goal.

In this post I write some snippets to demonstrate what other languages provide to prevent the exception raised by referencing to a null object.

Demonstration of the core problem again, comes below:

Person john = new Person("John", 54, new Address("Paris", "Champs-Elysee", 54));
Person joe = new Person("Joe", 54, null);

System.out.println(john.getAddress().getStreet()); // emits the street name
System.out.println(joe.getAddress().getStreet()); // raises NPE

And in case of hash, the same problem looks like that:

Map<String, Map> hsh = new HashMap<String, Map>();

// ... add some pairs

System.out.println(hsh.get("London").get("john"));
System.out.println(hsh.get("Paris").get("john")); // raises NPE

For testing I used a Person object which embeds an Address object which has some fields. I would like to get back the value of a field in the embedded object through the instance of Person.

Java

Java 8 introduced Optional type and method references. Combining these two we can safely traverse over the embedded objects without fearing to get NPE.

If one of the values is null, it returns 'N/A' by default.

String street = Optional.of(john)
        .map(Person::getAddress)
        .map(Address::getStreet)
        .orElse("N/A");
    System.out.println(street);
    street = Optional.of(joe)
        .map(Person::getAddress)
        .map(Address::getStreet)
        .orElse("N/A");
System.out.println(street);

In Java 8, a very handy method of Map got introduced, called getOrDefault. In case of it looks like as follows:

System.out.println(hsh.getOrDefault("London", new HashMap<String, Integer>()).getOrDefault("john", -1));
System.out.println(hsh.getOrDefault("Paris", new HashMap<String, Integer>()).getOrDefault("john", -1));

Groovy

Groovy brings safe navigation operator as part of the language.

john = new Person("john", 54, new Address("London", "Baker", 221))
println(john.address.street) // Baker
joe = new Person("joe", 54, null)
println(joe?.address?.street) // though address is null it is not raising NPE

Here there are two options, one would go for getAt and null propagation operator (?. ) is applied on the returned object. Unfortunately it does not return a default object. get method returns its second parameter if key does not exist.

println(cities.getAt('Paris')?.getAt('joe')?.getAt('age'))
println(cities.get('Paris', [:]).get('joe', [:]).get('age', -1))

Python

Python does not support null propagation on embedded objects, but it supports returning default values if key does not exist in the dictionary.

print(cities.get('London').get('joe'))
print(cities.get('Paris', {}).get('joe', 'N/A'))

C#

Safe navigation in C# looks like:

Console.WriteLine(john?.Address?.Street ?? "Portobello");
Console.WriteLine(joe?.Address?.Street ?? "Portobello");

The ?? operator ensures that in case of null or falsy value, the printed text will be the right operand.

Though C# provides TryGetValue which can be used for getting default values, though it is not available for chaining.

Javascript

Both the dot notation and index support are available in Javascript to read fields of an object. Since objects acts as a dictionary, index support can be used to reach fields.

console.log(((joe || {}).address || {}).street || 'Portobello');
console.log(((joe || {})['address'] || {})['street'] || 'Portobello' )

In this post we have got some ideas how to handle null propagation in different languages.

I took the latest versions of the languages. If you are awaire of additional solutions either in these languages or in others, feel free to fork-add-pr!

Codes can be found: https://github.com/torokmark/safe-navigation-in-languages