Have you ever been able to confidently write Java for hours without executing it? I think exploiting J8 and generic types can help with that :) Although I consider Java as a practical language, I've never really been a fan. But after my recent ventures into J8's functional features, I like it a little better! In this article, I'm going to demonstrate how one can leverage these features to write some clean and reliable Java code by using a specific example.
Functions can return a result of one type. But what if we wanted to return a result containing a value of one of two types? For instance, a remote service could return an Exception or a Result as a response. One way would be to wrap both of them inside a Response object and provide some methods to check which type of answer is available, and give them suitable naming/javadoc to help the programmer find out how to retrieve the appropriate answer to the request.
This is exactly the kind of *nonsense* we hope to avoid: contracts which aren't obviously visible at code or type level.
It would be nice to have the request return an object of type Either<Exception,Result>. Now this is very different from a Response object, as Either is very generic and as you will see, much more robust. In Either<L,R>, L and R denote Left and Right values. By convention, R is used to hold the "right" or correct result. Let's look at instances Left and Right later, and look at the Either methods first:
Here' a sample use:
getComponent returns an exception when the component is not found, and the component when it is. It uses Left<Exception,Result>(Exception e) and Right<Exception,Result>(Result r) constructors to construct an object of type Either<Exception,Result>. To achieve this specific use case, one might as well simply use Optional, but assume this code is on a remote service which offers responses using Either, and just hold on :)
Now that we have a response, we would want to do something useful with it - like print it! To examine the result, we use pattern matching!
Neat, isn't it? Here's the definition of instances Left and Right:
Now, what if we wanted to find a component's price using a component's ID? Say, by making another call which could fail?
Now, that we have the pieces to get a component and find it's price - where both of which return an either value of a different type, how does one compose them? This is where the magical bind comes in:
The bind ensures that if the response returned a failure (exception), the failure is propagated, else the correct computation takes place on the "Right" value. But not always do we have Either computations (in our context, failure prone), we might have simple functions which we would like to re-use like this:
How does one re-use this to apply a discount on our component's price wrapped inside an Either? This is where fmap is incredibly useful: to use functions of simple types and on an Either result. Here's a modified getComponentPrice which applies a discount:
Now, time to put these together: assume a product is made of components, and to a buy a product one would need to sum the prices of all products. The idea is: if any phase fails, the failure should be propagated. If a component is not available or it's price is a not available, it shouldn't be possible to buy the product!
How cool is that? Notice how all the core functions - getComponentPrice and getProductPrice - containing the important business logic do not contain any unnecessary and clutter-y error handling code. This is the real beauty of this pattern. All the error handling is done under the hood! Time to test it:
Works like a charm on the very second run! The problem with my first run was that I forgot to initialize something and at the only place I do an unsafe .get() (Guess where?) caused a NullPointerException. Yet another reason to use Optional everywhere.
The really nice part is that I spent more time coding, than debugging and shaping my code accordingly - very rewarding indeed! The types restrict you to a point that you are forced to screw up very less. This is the main reason I can confidently write code for hours without running it. Can you? :)
p.s. Congratulations, you have (hopefully) learnt to write some monadic code in Java: Either is a monad. This is was also my hidden agenda ;) All the code can be found here: https://github.com/nachivpn/fj