Kerflyn's Blog

Well… It's a blog!

From Optional to Monad with Guava

with 6 comments

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.

About these ads

Written by fsarradin

2011/12/05 at 00:16

6 Responses

Subscribe to comments with RSS.

  1. Great article, here we see the limitations in terms of readability of generics in Java !

    Matthieu D.

    2011/12/09 at 12:48

  2. Great article. I really need to learn more about more functional languages.

    I think you can simplify your getFrom() method, using Optional.fromNullable() ( http://docs.guava-libraries.googlecode.com/git-history/v11.0.1/javadoc/com/google/common/base/Optional.html#fromNullable(T) ):

    return Optional.fromNullable(map.get(key));

    Using this convenience factory method, you don’t really need the getFrom() method anymore.

    Also, I know that this was simply an example to demonstrate the use of Optional, but this code:

    process(getFrom(map, “a”).or(DEFAULT));

    could be replaced with Guava’s Objects.firstNonNull():

    process(Objects.firstNonNull(map.get(“a”), DEFAULT));

    As I see it, you shouldn’t use Optional *inside* your getProductFrom() method. It complicates the code, and checking for null is a lot cleaner in this case. I would simply work with the maps and null values, and *then* return an Optional.

    Etienne Neveu

    2012/02/11 at 17:18

    • @Etienne: Thank for your comment. You’re perfectly right when you’re writing that I’m overusing the Optional type in my code. But, one hidden questions behind this post concerns the usefulness of the Optional type in Guava. It’s fully justified with a language like Haskell as it doesn’t own a null reference. But with Java, this is not completely the case.

      The example I’ve put for getProductFrom() is a common example that shows the inefficiency of the Optional type in combination with a set of nested if structures. That’s what I’ve done here and that’s what you’ve pointed out. This example is also used to justify the use of monads.

      Nevertheless, there are many paradigms to learn, FP is the one that is the easiest to apply in Java, alternatively to the OOP. It may change your vision of the way you program and it may also improve the maintainability of your code.

      fsarradin

      2012/02/11 at 22:41

  3. […] a previous post, I’ve shown how to use the type Optional provided by Guava, in a view to create a monad. […]

  4. Optional is useful in Java too, even if only for removing possible execution paths. Code written using Optional is easier to read and test than code written with lots of conditional logic.

    • @benhardy: Indeed, I think there are cases where Optional alone is useful. But, it can also be a unnecessary abstraction to hide null references, as I show above.

      Nevertheless, I’m excited by the way Optional is evolving and used in Guava 12.

      1/ you have more and more methods that returns an Optional. This leads to more readable code :

      FluentIterable.from(elements).first().or(defaultValue);

      2/ Optional has a transform method (so its a functor, not a monad yet). Thus you can apply a transformation on the value contained in the Optional. If there is no value, the transformation is not applied:

      FluentIterable.from(products).first().transform(GET_PRICE).or(0.0);

      I’m really convinced, that through this kind of evolutions, Optional is a useful class.

      fsarradin

      2012/05/09 at 23:20


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 507 other followers

%d bloggers like this: