Posts Tagged ‘monad’
From Optional to Monad with Guava
Guava is a kind of Swiss Army knife API proposed by Google for Java 5 and more. It contains many functionalities that help the developer in its every day life. Guava proposes also a basic functional programming API. You can represent almost statically typed functions with it, map functions on collections, compose them, etc. Since its version 10.0, Guava has added a new class called Optional
. It’s an equivalent to the types Option
in Scala or Maybe
in Haskell. Optional
aims to represent the existence of a value or not. In a sense, this class is a generalization of the Null Object pattern.
In this article, we see the Optional
class in action through two use cases involving java.util.Map
instances. The first use case is a very basic one. It shows how to use the Optional
class. The second use case is based on a problem I’ve met in a real project. We’ll see if Optional
is helpful in this case.
Optional vs. null
Suppose that you want to get a value from a Map
. The value is supposed to be located under the key "a"
, but you aren’t sure. In the case where the key "a"
doesn’t exist, the Map implementation is supposed to return null
. But, you want to continue with a default value.
Here, you have two solutions. This one
Integer value = map.get("a"); if (value == null) { value = DEFAULT; } process(value);
And, this one
Integer value = map.get("a"); process(value == null ? DEFAULT : value);
In order to use the Optional
class, we need to define a new accessor for Map
instances.
public static <K, V> Optional<V> getFrom(Map<K, V> map, K key) { Optional.fromNullable(maps.get(key)) }
We notice that you can instantiate Optional by different ways: 1/ by a call to Optional.absent()
if there is nothing to return (except the Optional
instance), 2/ by of call to Optional.of(value)
if you want to return a value. It sounds logical that an accessor to Map
returns an Optional
, because you aren’t sure that the given key exists in the Map
instance.
Below is the same program but using Optional
class.
process(getFrom(map, "a").or(DEFAULT));
With this code, we don’t see null
anymore.
Going further: Optional to the limit
Now, we suppose that we develop a application based on a set of products differentiated by a unique identifier. The products are organized by product identifier, supplier, city, and country. In order to stock these products, imbricated Map
are used: the first level uses the country, the second level uses the city, the third level uses the supplier, and the fourth level uses the product identifier to give access to the product. Here is the code you get with no use of Optional
class (please, don’t do this at work! Seriously, don’t do this!)
public Product getProductFrom( Map<String, Map<String, Map<String, Map<String, Product>>>> productsByCountry, String country, String city, String supplier, String code) { Map<String, Map<String, Map<String, Product>>> productsByCity = productsByCountry.get(country); if (productsByCity != null) { Map<String, Map<String, Product>> productsBySupplier = productsByCity.get(city); if (productsBySupplier != null) { Map<String, Product> productsByCode = productsBySupplier.get(supplier); if (productsByCode != null) { return productByCode.get(code); } } } return null; }
This doesn’t sounds great, is it?
Now, below is the solution using Optional
class.
public Optional<Product> getProductFrom( Map<String, Map<String, Map<String, Map<String, Product>>>> productsByCountry, String country, String city, String supplier, String code) { Optional<Map<String, Map<String, Map<String, Product>>>> productsByCity = getFrom(productsByCountry, country); if (productsByCity.isPresent()) { Optional<Map<String, Map<String, Product>>> productsBySupplier = getFrom(productsByCity.get(), city); if (productsBySupplier.isPresent()) { Optional<Map<String, Product>> productsByCode = getFrom(productsBySupplier.get(), supplier); if (productsByCode.isPresent()) { return getFrom(productByCode.get(), code); } } } return Optional.absent(); }
It looks ugly too!
In a view to simplified this implementation, I propose to introduce the notion of monad.
Option(al) monad
A monad is a programming structure with two operations: unit and bind. Applied to the Optional
class, unit converts a value into an Optional
instance and bind applied to an Optional
a function from value to Optional
. Below, you’ve there definitions in Java:
public class OptionalMonad { public static <T> Optional<T> unit(T value) { return Optional.of(value); } public static <T, U> Optional<U> bind( Optional<T> value, Function<T, Optional<U>> function) { if (value.isPresent()) return function.apply(value.get()); else return Optional.absent(); } }
Notice that bind checks the presence of a value before to apply the function.
Now, to be used by our example in the section above, we need to define a function that will be used as parameter for the bind operator. This function is based on the method getFrom
, previously defined, which gives access to a value of a Map
from a given key.
public static <K, V> Function<Map<K, V>, Optional<V>> getFromKey(final K key) { return new Function<Map<K, V>, Optional<V>>() { @Override public Optional<V> apply(Map<K, V> map) { return getFrom(map, key); } }; }
Here is the new code
public Optional<Product> getProductFrom( Map<String, Map<String, Map<String, Map<String, Product>>>> productsByCountry, String country, String city, String supplier, String code) { Optional<Map<String, Map<String, Map<String, Product>>>> productsByCity = bind(unit(productsByCountry), Maps2.<String, Map<String, Map<String, Map<String, Product>>>>getFromKey(country)); Optional<Map<String, Map<String, Product>>> productsBySupplier = bind(productsByCity, Maps2.<String, Map<String, Map<String, Product>>>getFromKey(city)); Optional<Map<String, Product>> productsByCode = bind(productsBySupplier, Maps2.<String, Map<String, Product>>getFromKey(supplier)); Optional<Product> product = bind(productsByCode, Maps2.<String, Product>getFromKey(code)); return product; }
Or more directly
public Optional<Product> getProductFrom( Map<String, Map<String, Map<String, Map<String, Product>>>> productsByCountry, String country, String city, String supplier, String code) { return bind(bind(bind(bind(unit(productsByCountry), Maps2.<String, Map<String, Map<String, Map<String, Product>>>>getFromKey(country)), Maps2.<String, Map<String, Map<String, Product>>>getFromKey(city)), Maps2.<String, Map<String, Product>>getFromKey(supplier)), Maps2.<String, Product>getFromKey(code)); }
OK! It’s weird too. We have to ‘fight’ with inline recursive calls of the bind
method and also with Java generics. The Java type inference system isn’t sufficiently powerful to guess them. To reduce occurrences of generics in this code, we could have written a specific version of getFromKey
method for each part of the given Map
: getFromCountry
, getFromCity
, getFromSupplier
, etc. This moves and distributes the complexity of the code in those methods.
public Function< Map<String, Map<String, Map<String, Map<String, Product>>>>, Optional<Map<String, Map<String, Map<String, Product>>>> > getFromCountry(String country) { // type inference system knows what to do here return Maps2.getFromKey(country); } // declaration of the other methods here // ... public Optional<Product> getProductFrom( Map<String, Map<String, Map<String, Map<String, Product>>>> productsByCountry, String country, String city, String supplier, String code) { return bind( bind( bind( bind( unit(productsByCountry), getFromCountry(country)), getFromCity(city)), getFromSupplier(supplier)), getFromCode(code)); }
The positive side lies into the fact that the if imbrications have disappeared and the code is linear. Notice that due to the bind
operator, if one of those call to getFromKey
method returns an Optional.absent()
, then all the following call to bind
will also return an Optional.absent()
.
Is there an happy end for this?
It’s difficult to do something better in Java by using the Optional
class (unless you have better solution). By considering another JVM language, you’ve below a solution in Scala.
def getProductFrom(products: Map[String, Map[String, Map[String, Map[String, Product]]]], country: String, city: String, supplier: String, code: String): Option[Product] = { for ( cities <- products.get(country); suppliers <- cities.get(city); codes <- vendors.get(supplier); product <- codes.get(code) ) yield product }
Here, the for
structure provides syntactic sugar to do something close to imperative programming. But in fact, each line in this for
structure does the same thing as our previous implementations using the bind
operator. The get
method here acts the same way as our getFrom
method by returning an instance of the type Option
. The type Option
in Sala is equivalent to the type Optional
in Guava. So when you call our Scala version of getProductFrom
, if all your parameters appears in the Map
, it returns a product. But if one of the parameter isn’t present, you get the default value.
In Other JVM languages proposes the safe-navigation or null-safe operator value?.method()
, like in Groovy, in Fantom, in Gosu, etc. It checks if the value isn’t null
before calling the method.
// get a product or null product = products?.get(country)?.get(city)?.get(supplier)?.get(code)
For the kind of problems presented in this post, the null-safe operator does a better job than the monad approach. In fact, monads represent a programming structure with a really wider scope. Associated with types other than Optional
, you can extend the application field of monads and get the necessary expressivity to explore non-determinism, concurrent programming, handle of errors, etc.
Conclusion
So, we’ve seen the class Optional
provided by Guava in two use cases. For the simple case, we’ve seen how Optional
helps to make null reference disappears. But for the complex case, we’ve seen that Optional
alone provides no real advantage. Optional
class can make the approach a little easier if we introduce the notion of Optional
monad. Thereby, not only the null references disappear but also the recursive if structure. But, we have to fight with the Java generics. And even after this, the code is still hard to read.
It seems clear that to explore a recursive data structure made of Map
with Java, you have to choose between to fight with if statements or to fight with generics. But, Java may be not the good language for this kind of problem. In other hand, you should ask yourself if using such a structure is a judicious choice?
EDIT 2012-02-23: simplified getFrom method + corrected the returned type from getFromCountry method.