A simple program

Lets define a simple program that manages metadata about some files stored on a local file system and allows users to read those files from disk and process them in some manner.

We could start with a class that manages metadata about the files

In this class everything is mutable, and we’ve followed the standard Java pattern of supplying Getters and Setters (via Lombok @Getter and @Setter annotations).

A surprising amount of hidden complexity

The code is barely 20 lines long it is already waaay more complex than it needs to be. There is an inter-dependency between the getContents method and the loadContents method that we’ve left for client code to resolve.

loadContents needs to be called before contents is accessed.

Will getContents() do what we expect?

We could create a FileManager class that accepts DataFileMeta to process and stores their contents by customerId in a HashMap.

The code review bottlekneck

If you were to submit this code for code review, you may get back a some informative feedback that the call to FileManager::process will result in null values being stored in our HashMap and that you need to call metadata.loadContents inside the process method. Alternatively the feedback may ask that you move the call to FileManager::process to somewhere further down the call stack to after a point where loadContents has been called on the application DataFileMetadata files. Or perhaps you need to check

if(metadata.getContents()==null) metadata.loadContents();

Or you could just be very lucky and the code above may just work.

If you have ever worked on a large and complex application where code reviews are a bottlekneck to feature delivery, where only a small number of deep experts in the applications code can adequately assess the impact of a code change — this type of logic and coding style is often a core reason why. Even in this trivial looking class we have failed to encapsulate a core relationship between two methods properly, allowed it leak out, and abandoned it’s resolution to the god’s of tribal team logic and knowledge.

Encapsulating the relationship

We can fix this inter-dependency by ensuring that loadContents is called prior to getContents and then hiding the loadContents method outside of the class.

While this approach will undoubtedly make client code extracting contents simpler (when all files are available and loadable without errors), it will also impose a performance penalty as we now load every File into memory when it’s metadata Object is created.

[Functional Concept] Laziness : to the rescue!

We can solve the performance problem of loading every file into memory on object creation by moving the call to load to an overloaded get method :-

Now we are beginning to get somewhere! We also don’t need to load the contents from disk everytime getContents is called, so we can introduce a null check to only load the contents on the first call.

Lazy enscapsulated verbose code!

Now we have successfully encapsulated the file management aspect of the DataFileMetadata class and got the performance acceptable by introducing laziness. We have implemented all this in an imperative fashion and as a result the code is reasonably verbose.

We can clean this code up by introducing some functional types — and this refactoring will help us when we attempt to solve some other downsides of this current class implementation in future episodes of this series.

Supplier : the lazy interface

The JDK’s Supplier interface is a SAM Functional Interface that represents something that can be supplied to us lazily in the Future. We could define a Supplier instance that pointed to the method to load the data from File.

e.g.

Suppplier<String> contents = this::loadFromFile;

That is, we can create a Supplier that points to the method reference for loading the data from a File. In practice we wouldn’t want to use the loadFromFile method, as the loadContents method does some important Exception handling and translation.

But unfortunately, the return type on the loadContents method is currently void, which means it isn’t suitable for this task without some refactoring.

So let’s change it to return a String, and to directly return the contents of the file.

Now we can write

Suppplier<String> contents = this::loadContents;

Which in turn means we can simplify our getContents method too.

B ut.. oh no! now we’ve lost our caching!

[Functional Sub Concept] Memoization : to the rescue!

Memoization is closely related to laziness in functional programming, and refers to the ability to cache and resuse lazily computed values. It is pretty straightforward to implement Memoization for Suppliers. For example using ConcurrentHashMap in Java we could define a Memoization function for Suppliers like so

public static <T> Supplier<T> memoizeSupplier(final Supplier<T> s) {

final Map<Long,T> lazy = new ConcurrentHashMap<>();

return () -> lazy.computeIfAbsent(1l, i-> s.get());

}

Any Supplier that has passed through the MemoizeSupplier method automatically caches it’s result. That is

int called = 0; Supplier<Integer> lazyCaching = memoizeSupplier(()->called++);

Will always return 0 , no matter how often get() is called (as called is 0 and returned before being post-incremented), and called will always remain 1 once get() has been called at least once. Let’s see that in action :

Running the code above results in

Cyclops : no need to roll your own

The cyclops library for functional programming in Java provides an (more efficient) implementation for us and we can add it to our Maven our Gradle class paths

Maven

<dependency>

<groupId>com.oath.cyclops</groupId>

<artifactId>cyclops</artifactId>

<version>10.0.4</version>

</dependency>

Gradle

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

And use the Memoize class directly in our code.

Bringing caching back!

We can re-introduce caching to our code by making use of the Memoize class in cyclops.

Introduction to Eval : a lazily evaluated memoizing datatype

Working with lazily computed values in both caching and non-caching manners is such a common pattern that cyclops has a datatype entirely dedicated to this purpose. It is called Eval. We can use Eval.later to create a lazily evaluated memoizing method reference.

A less verbose version

With loadContents reduced in scope from variable assignment and exception management, inlining loadFromFile introduces very little additional cognitive overhead.

From mutable to immutable

In our initial implementation the contents field had to be mutable to support it’s lazy population (via the getContents method). Now, contents could (should) be made immutable (👏😄) . This is our next topic : functional programming with immutable objects in Java! Followed by Functional Composition, Null Handling, Error Handling, Concurrency and Immutable Collections.