Vavr’s flatMap in action

Jaroslaw Kijanowski
SoftwareMill Tech Blog
5 min readDec 19, 2018

--

TL;DR Whenever your cargo is inside a ship carried by another ship — flatMap that ship!

https://commons.wikimedia.org/wiki/File:Cardinal_and_Raven_on_Blue_Marlin.jpg

Programming is often about transforming data from one type to another. Vavr provides the map method which is invoked during a so-called happy path: you map a Try’s success, an Option’s value, an Either’s right, a Future’s successful result and finally a List’s element.

Every time you type map, your IDE provides you with a list of suggestions and typically a flatMap method is present among them. If you’re curious, when and how to use this mysterious thing, here are some examples.

1) flatMap that List of Lists

In this example flatMap is used to first map each element of the listOfLists List, called innerList, to another List. Every innerList element, a List of Strings is left untouched — that’s what x -> x stands for. Another way of returning a function’s input in a map operation is to use java.util.function.Function.identity().
The second task of flatMap is to flatten the result. In case of a List of Lists, this means to return a single List with elements from all the inner Lists: [[a, b], [c, d], [e, f, g]] becomes [a, b, c, d, e, f, g].

A slightly more complex version of this example is to apply a map function to each of the inner List’s element, like toLowerCase().

To sum it up: flatMap on a List of Lists applies a map function on each element of the outer List and returns one List.

2) flatMap that List of Options

Similarly flatMap is able to flatten a List of Options of String to return a List of Strings. The neat thing here is that only a success is processed (mapped) — in case of an Option a success is when it is defined.

Obviously we can also apply a map function on every element wrapped by an Option. Here it is the toLowerCase()method:

To sum it up: flatMap on a List of Options applies a map function on each defined Option and returns one List of every wrapped element.

3) flatMap that List of Tries

Last basic example and we’re ready to move to the more fancy stuff. A List of Try objects, each wrapping a failure or a success (String value), is first mapped. To keep it short, Function.identity() leaves the successes and filters out any failures leaving us with a List of successful Try objects. Finally this list is flattened, which means to extract the success values from the Try objects and return them as a List.

To sum it up: flatMap applied to a List of Tries runs a map function on each Try.Success and returns a List of every wrapped element.

These are very basic examples doing trivial map operations. They just flatten the collection of collections returning one single collection. This can be applied to other functional data structures like Array, Vector, Queue, Set and Map.

4) flatMap that Try of Tries

Once you get used to functional programming with Vavr and start using Try in public methods you may soon end up with a far more common scenario, where from within a Try context you call another method returning a Try:

The method wrapThrowOnXYZ() returns a Try. In case of a success we map the wrapped value to another Try by calling anotherTryWrapper(). And this leaves us with a Try of a Try. There are three possible results:
- the outer Try is a failure,
- the outer Try is a success but the the inner Try is a failure,
- the outer Try is a success and the the inner Try is a success.

To extract the result (Try of a Try) we cannot simply call get() on the outer Try. First we need to check if it is a success with isSuccess(). Otherwise we could end up with an exception being thrown, if the Try would wrap a failure and there would go our “don’t throw, return a Try” professional programming style.

This can be fixed with flatMap:

flatMap will either return a success wrapping a String or one of the two possible failures: an exception from wrapThrowOnXYZ() or an exception from anotherTryWrapper().

To sum it up: flatMap on a Try of Tries returns a Try.Success if all the Tries are successful or the first Try.Failure encountered in the chain.

5) flatMap that Option of Option

The next example shows a very similar approach, just with Options.
Once you’re methods return Options wrapping a value or an Option.None, you’ll encounter this situation: from within an Option context you provide a mapper returning also an Option:

Again we’re left with 3 possible results:
- the outer Option is a None,
- the outer Option is a Some, but the inner Option is a None,
- the outer Option is a Some and the inner Option is a Some.

We cannot call get() on the outer Option to unpack the inner Option, without calling isDefined() first. Otherwise an exception is thrown if the Option is a None. What we want is just one Option being either None or Some. And here flatMap shines again.

To sum it up: flatMap applied on an Option of another Option results in an Option.Some if all Options are Some or an Option.None otherwise.

6) flatMap that Either of Either

As stated earlier, map applies a mapping function on a success. In case of an Either, this is the Right value. Having said that, let’s see how to apply flatMap on an Either.

The outerEither Either is
- an Either.Right wrapping the age String, if age is not null,
- an Either.Left wrapping “empty” if age is null.

Applying flatMap returns
- an Either.Left wrapping “empty” if outerEither is Either.Left,
- an Either.Left wrapping a String that could not be parsed to an Integer,
- an Either.Right wrapping an Integer if age could be parsed successfully.

To sum it up: flatMap on an Either of another Either results in an Either.Right if all Eithers are Right or an Either.Left wrapping the first Left encountered.

All the examples are available on GitHub and there is also a test suite provided to play around with different inputs.

To finally sum it up: there are many ways flatMap can be used to process data. However now it should be clear what it means to map a List element, a Try.Success, an Option.Some,a completed Future or an Either.Right and then flatten the result.

Looking for Scala and Java Experts?

Contact us!

We will make technology work for your business. See the projects we have successfully delivered.

--

--

Java consultant having experience with the Kafka ecosystem, Cassandra as well as GCP and AWS cloud providers. https://pl.linkedin.com/in/jaroslawkijanowski