Dysfunctional programming in Java 4 : No nulls allowed

From dysfunctional to functional

John McClean
11 min readOct 27, 2018

In the previous articles in this series we investigated the benefits of refactoring an imperative application to a more functional style introducing laziness , immutability and functional composition.

A radical suggestion to improve code quality, design quality, reduce production errors and aid the adoption of functional thinking — ban the assignment of nulls! (⛔️😲🤔🚫)

It is good way to help engineers think about their data structures in a way that jives well with the functional paradigm. It also results in better Object Oriented designs.

Photo by Brett Ritchie on Unsplash

It’s not about Optional

Reaching for Optional seems like a reasonable solution when faced with an edict to avoid the assignment of null values, but this shouldn’t be our first port of call. Writing on StackOverflow, Java architect Brian Goetz give a hint of the rationale for the introduction of Optional and it’s expected usage :-

Of course, people will do what they want. But we did have a clear intention when adding this feature, and it was not to be a general purpose Maybe or Some type, as much as many people would have liked us to do so. Our intention was to provide a limited mechanism for library method return types where there needed to be a clear way to represent “no result”, and using null for such was overwhelmingly likely to cause errors.

Limiting the use of Optional or Option types to method and function return types is actually a pretty sound design guideline (or rule, if you insist, but only if you are also comfortable breaking it occasionally). Not because of any appeal to authority on the behalf of the JDKs authors — you know your own code and teams style much better. But because in an Object Oriented language setting fields to null is a sign that those fields could be more better represented as a non-nullable value in some sub-class. Equally, Optional parameters can often be better handled and represented by alternative method declarations.

Don’t allow null fields

If you find yourself declaring a lot of fields as null, this is a pretty good sign that your Object model needs reworking. This is a good example of where the best practices of Object Oriented design converge with core functional principles. That is, that illegal states should be unrepresentable in our code — if a field is present in a class we should not have to worry about a NullPointerException when an attempt is made to access it.

Back to our application!

We have our DataFileMetadata class that represents a Data File stored in some location for a customer.

A common development practice is, unfortunately, to continue to expand our existing classes as new requirements and feature requests come in, rather than to refactor, rethink and rework our designs.

Not just local files

A new requirement. Customers can manage their own files on their own public facing Webservers. We need to support loading data from a supplied URL.

If we add just this one additional field to our DataFileMetadata class, it will result in significantly more complexity making the code for that class harder to reason about.

The class has become significantly larger! Let’s unpack why

New constructors required

We’ve added just one additional field

But.. file can be null, and url can be null .

That implies two separate constructors (and we still need the Lombok AllArgsConstructor for the Withers to work).

loadContents becomes much more complex!

Before it had two code paths

  1. Load the file and return
  2. Throw an Exception if file loading failed

Now there are four!

  1. If the file is present load and return
  2. If the url is present load and return
  3. If neither are present throw an Exception
  4. If something goes wrong throw an Exception

Can Optional help us?

Not really. Optional captures the fact that file may be missing, or url may be missing — but it doesn’t help us at all in terms of the relationships between them.

That is

  • only file or url should have a value at any given time
  • The other one should be null
  • but both should not be null.

This is the major reason why Optional is rarely a good fit for use in Fields! It rarely captures the complexity of the relationships between the fields and pushes the complexity elsewhere. loadContents will be exactly the same — we will just check isPresent rather than if (x==null).

Can other Functional Data types help?

There is a functional data type that will allow us to represent this relationship accurately : Either. Either represents a value that can be of one two defined types.

Either<URL,File>

We could replace both file and url fields with a single field that encapsulates that only one or the other type should be present

If this is the extent of the changes or differences between our data model for local files and remote URLs this may be an acceptable solution.

A note on how Either is implemented

In cyclops, Either is defined as an interface with two types

Either≤L,R> 

and has two concrete implementations each with a single type.

Left<L>Right<R>
ns

In other words we can represent the disjoint nature of the relationship between the Left and Right type as separate classes in a shared inheritance hierarchy.

Inheritance too the rescue!

A more common solution in traditional Java is to leverage — inheritance! We can make DataFileMetadata an abstract base class with two different implementations, one for local files and one for remote URLs.

We can implement the abstract class for DataFileMetadata in just a few lines of code (note the absence of the Wither annotation, which does not play well with inheritance).

The implementation for the File supporting sub-class is also very short.

Importantly the implementation of loadContents reverts to the version we had originally before URL support was introduced — only two branches!

The implementation of the URL supporting sub-class is really short too, and has a simple loadContents implementation also with only two branches.

Nullable fields are a design smell

You should never have nullable fields, it is always a symptom of a design problem (performance use cases excepted). There are two options to fix it :

  1. Make use of a Functional Sum Type (like Either, LazyEither3 and so on in Cyclops)
  2. Take advantage of inheritance and composition to define classes where redundant fields do not exist.

By banning and enforcing nullable fields you will improve the overall quality of your applications designs, remove the need for defensive null checks and eliminate Runtime NPEs caused by accessing those fields.

There are other places where we may be tempted to use null, but please don’t!

I/O issues and DataFileMetadata

One challenge we may face is that when we atttempt to access the contentds in DataFileMetadata instance for the first time, the remote server or even local file system may be temporarily unavailable. This will cause a blow up and an Exception will be thrown that we will have to handle correctly in any client code. Failure to handle it will result in our application failing.

For example if I write some test code that attempts to access content from a fake URL

URLDataFileMetadata meta = new URLDataFileMetadata(10l,"url",new URL("http://oath23232.com"));
System.out.println(meta.getContents());

Running it results in a horrendous stack trace.

A hacky way around this may be to attempt to return null from loadContents instead when loading fails, temporarily swapping one problem for another.

Updating our client code to resolve the contents from Eval

System.out.println(meta.getContents()
.get());

Results in

Which means any calling code attempting to use the contents of a DataFileMetadata instance has to defensively check for Nulls or risk runtime exceptions.

There is a better option here (pun intended). The JDK does have a type that represents a value that can either be present or absent — Optional.

What’s wrong with Optional?

The concept of designing APIs and implementations that make illegal states unrepresentable in your code is discussed more thoroughly in “We need safer APIs *and* safer Implementatons”. But it is worth recapping the downside of Optional here

When we run this code :

we will get a RuntimeException

To avoid this we should check if an Optional isPresent before calling get(), which is pretty similar to how we defensively check for null in our code basis today. It would be better if our APIs didn’t expose methods like Optional::get that throw RuntimeExceptions!

Cyclops defines a safe Optional type called Option. Instead of Optional::get you can use overloaded methods such as Option::orElse to extract a value from the Option type safely.

Let’s plugin in Option

The changes to our loadContents implementation are relatively minor, it’s just a case of creating an Option.some instance when a value is successfully downloaded otherwise returning Option.none

The downside is that we very quickly heading in the direction of generics hell in terms of our variable definition for contents

Eeek! This is not good and we can do better!

One option to improve the signature is to perform a concatMap operation (like flatMap across Iterable types)

And that certainly cuts down on the generics. When we run the failing code, however, contents will resolve to null again as the flatted Option.none is converted into null.

System.out.println(meta.getContents()
.get());

A better alternative is to change the top level type to one which accounts for the fact that it may or may not be present. I.e. Make contents an Option.

But wait — didn’t we just say not to use Optional-like types for fields? And yes, as a good rule of thumb (or guideline) we shouldn’t. But… rules unlike thumbs (👍 but especially like guidelines) are made to be broken. If we are confident that what we are doing is right. Is there a better Object Oriented design that will solve the problem of the unavailable contents for us? Not one that is immediately obvious to me..

Option as a field

It is easy enough to declare contents as an Option, but what should the right hand side of the assignment be?

We want to load the contents lazily, but Option is a strict or eager type — we need to know at Object creation time if it should be an Option.some (present) or Option.none (not there).

If only we had some sort of lazy Option type to use instead (🤔🤓🧐💡)…

Definitely Maybe

As luck would have it, there is a lazy Option type in Cyclops and (like it’s lazy Haskell cousin) it is called Maybe (💯). Maybe allows us to create an Option when we don’t know whether it will ultimately be a Some or a None at creation time. We can use Maybe to help us write the right hand side of the assignment.

Now we have a contents variable that

  1. Is lazily populated on first access
  2. Is only populated once due to Memoization
  3. Does not throw Exceptions if the I/O data is unavailable
  4. Represents the fact the contents data maybe present or absent

Do we still want memoization?

It is possible, that due to the fact loading may fail that we may prefer to load the contents afresh each time we access it. In that case we can switch

Eval.later

To

Eval.always

Which turns off caching.

With Memoization still on, our complete entity code looks like this :-

From Functional to Reactive

In Dysfunctional programming in Java 3 : Functional Composition we built some client code that could load the content from a large number of DataFileMetadata instances asynchronously and in parallel.

Rather than turn memoziation off altogether, we could introduce a mechanism to help us reload the contents for those DataFileMetadata instances where that failed (and the contents variable is an instance of Maybe.nothing).

We could do this by taking advantage of Cyclops’ CompletableMaybe type. CompletableMaybe is a reactive Option type. With CompletableMaybe we can return to the user a Maybe instance for further processing, but populate it’s completed value asynchronously — which will then get pushed down through the users pipeline.

Working with the reactive Maybe

Client code can tee up additional method calls on the Maybe returned by loadAsync that will be executed asynchronously (on the same thread by which the result is populated), without blocking the current thread of execution.

E.g.

In the code above we lazily (but not reactively) load the contents from the meta instance. In otherwords meta.getContents() blocks the current thread. If that fails we load the contents asynchronously. Reassigning contents (eek!) to a reactive Maybe instance. The peek method that logs out the ‘flaky’ contents is executed asynchronously. Calling further operations on the contents instance doesn’t block the current thread (unless we call a method like orElse, or isPresent which needs to resolve the ultimate value).

Working with Reactor

Rather than use a CompletableMaybe as the return type in our loadAsync method we could make use of a Mono from Reactor. Mono is a reactive type that represents a delayed task that results in a single value or an Exception. We can connect a Mono to a Maybe via the fromPublisher method.

If the Mono succeeds we will get a Maybe with the successful value, if the Mono fails we will instead get an empty Maybe (Maybe.none).

Getting Cyclops

If you are following along, you can install the cyclops library for functional programming in Java by adding it to our Maven our Gradle class paths

Maven

<dependency>
<groupId>com.oath.cyclops</groupId>
<artifactId>cyclops</artifactId>
<version>10.0.1</version>
</dependency>

Gradle

compile group: 'com.oath.cyclops', name: 'cyclops', version: '10.0.1'

--

--

John McClean

Architecture @ Verizon Media. Maintainer of Cyclops. Twitter @johnmcclean_ie