DEV Community

Cover image for In praise of Vavr's Option
David Schmitz
David Schmitz

Posted on

In praise of Vavr's Option

Null is sometimes considered the billion dollar mistake. One could argue about this forever, but certainly, null has lead to a lot of awful code.

Most functional programming languages offer a concept called Option or Maybe to deal with the presence or absence of a value, thus avoiding null. Wikipedia defines the Option type as follows:

In programming languages (more so functional programming languages) and type theory, an option type or maybe type is a polymorphic type that represents encapsulation of an optional value; e.g., it is used as the return type of functions which may or may not return a meaningful value when they are applied.

This short post gives praise to the Vavr version of Option. We show how to use it and show its advantages over JDK8 Optional.

Vavr - Elevator pitch

Vavr, previously known as Javaslang, is a lightweight library that brings Scala-like features to Java 8 projects. It focuses on providing a great developer experience both through consistent APIs and extensive documentation.

Vavr offers many abstractions such as functional data structures, value types like Lazy and Either and structural decomposition (a.k.a. pattern matching on objects). Here we'll only highlight the Vavr Option.

If you have ever yearned for really good immutable and persistent collections, working value types, but could not move to Scala and friends because you are working on a brownfield project...then Vavr might just be your fix.

Optional FTW

Java 8 introduced Optional to handle the absence or presence of a value. Without Optional, when you face a method like this

public User findUser(String id) {
  ...
}
Enter fullscreen mode Exit fullscreen mode

you need to rely on Javadoc or annotations like @NotNull to decipher if that method returns a null.

Using Optional things can be stated quite explicitly:

public Optional<User> findUser(String id) {
  ...
}
Enter fullscreen mode Exit fullscreen mode

This literally says "sometimes no User is returned". null-safe. Say "adios" to NullPointerExceptions.

However...

As with all of Java 8's functional interfaces, Optionals API is rather spartanic, just a dozen methods, with "highlights" such as

Optional.ofNullable(user)
Enter fullscreen mode Exit fullscreen mode

If you are used to the expressivness of Scala's Option, then you will find Optional rather disappointing.

Furthermore, Optional is not serializable and should neither be used as an argument type nor stored as a field - at least according to the design goals of the JDK experts (http://mail.openjdk.java.net/pipermail/jdk8-dev/2013-September/003274.html).

Vavr Option to the rescue

The Vavr Option takes a different approach. See the following image, that illustrates the type hierarchy.

Option type hierarchy

Option follows the design of other functional programming languages, representing absence and presence by distinct classes, None and Some respectively. Thus avoiding the ofNullable nonsense.

Option.of(user)
Enter fullscreen mode Exit fullscreen mode

And the result would either be a Some<User> or a None<User>.

Internally absence is represented as null, so you if you wanted to wrap a null, you need to use

Option.some(null)
Enter fullscreen mode Exit fullscreen mode

although I do not recommend this approach. Just try the following snippet and you will see what I mean

Option.<String>some(null)
      .map(String::toUpperCase);
Enter fullscreen mode Exit fullscreen mode

Option is tightly integrated with Vavr's Value and Iterable types. This allows for a very consistent API. You can basically treat an Option like a collection with zero or one elements.

This might sound like a small thing, but consider this JDK8 Optional example.
We have a list of users.

List<User> users = new ArrayList<>(...);
Enter fullscreen mode Exit fullscreen mode

And now an Optional<User> which we want to add to the list.

Optional<User> optionalUser = Optional.ofNullable(user);

optionalUser.map(users::add);
Enter fullscreen mode Exit fullscreen mode

The intention is lost in the baroque syntax enforced by JDK8 Collection and Optional API.

Vavr's Option allows for a much cleaner syntax (note that we are using io.vavr.collection.List<T> not java.util.List<T>).

List<User> users = List.of(...);

Option<User> optionUser = Option.of(user);

List<User> moreUsers = users.appendAll(optionUser);
Enter fullscreen mode Exit fullscreen mode

Vavr treats Some<T> as a collection with one element, and None<T> as an empty collection, leading to cleaner code. In addition, note that a new list is created, because Vavr collections are immutable and persistent - a topic for a different day.

Option has more syntactic sugar for us:

Option<String> driverName = Option.when(age > 18, this::loadDrivingPermit)
                                  // Option<DrivingPermit>
                                  .peek(System.out::println)
                                  // Print it to the console
                                  .map(DrivingPermit::getDriverName)
                                  // Fetch the driver's name
                                  .peek(System.out::println);
                                  // Print it to the console
Enter fullscreen mode Exit fullscreen mode

Of course, as I said, this is basically sugar, but anything that reduced boilerplate code is highly appreciated.

Option is thightly integrated into Vavr's overall API and architecture. You can easily combine it with Vavr's Try monad, that helps dealing with exceptions in a functional way. Take the following example.

Option<Configuration> config = Try.of(Configuration::load)
                                  .toOption();
Enter fullscreen mode Exit fullscreen mode

We Try to load a Configuration and convert the result to Option. If an exception is thrown, then the
result is None otherwise it is Some.

Finally, you can use Vavr's pattern matching to decompose an Option

Match(option).of(
   Case($Some($()), String::toUpperCase),
   Case($None(),    () -> ""));
Enter fullscreen mode Exit fullscreen mode

If you have ever coded in a functional programming language, then this should be familiar to you. We basically Match the option against two patterns $Some($()) and $None(). Depending on the matched pattern we either convert the string to uppercase or return an empty string.

Using Vavr Jackson you can even use Option and all other Vavr datatypes over the wire. For Spring Boot projects you only need to declare the module such as:

@Bean
public Module vavrModule() {
    return new VavrModule();
}
Enter fullscreen mode Exit fullscreen mode

Summary

I hope this short post illustrates the usefulness of Vavr and its Option abstraction.

Vavr as a library offers many amazing extensions for object-functional programming in Java, even for brownfield projects. You can leverage its utilities where they make sense and need not migrate to Scala or similar platforms to reap at least some benefits of functional programming.

Of course, this is all syntactic sugar. But as any good library, Vavr fixes things, the core JDK cannot take care of so easily without breaking a lot of code.

Future posts will cover its other amazing features like pattern matching, property based testing, collections and other functional enhancements.

Latest comments (2)

Collapse
 
matteo_traina profile image
Matteo Traina • Edited

I like the library and really missing immutable persistent collection in Java: my only concern is the confusion that a different but homonymous class, like List, can create in a team.
On the other hand this is the price to pay if you want to write something interoperable with the existing API but that can change and enhance the original design.

Thanks for the nice write up.

Collapse
 
koenighotze profile image
David Schmitz

Thanks for the kind words. In fact, I share your concern...but we've found that we can easily use multiple libraries like Vavr in parallel, we just decide on a per-component or per-microservice level which way to go.

As Vavr is really non-invasive, there is no actual risk.