A question we got recently on Twitter was about how to convert the Future / For Comprehension example in Daniel Westheide’s Neophytes Guide to Scala: Welcome to The Future to Java using Cyclops.
Preparing a cappuccino
In that example we want to perform the following steps to brew a cappuccino. First synchronously and then asynchronously using Futures.
- Grind the required coffee beans
- Heat some water
- Brew an espresso using the ground coffee and the heated water
- Froth some milk
- Combine the espresso and the frothed milk to a cappuccino
Both the synchronous & asynchronous examples in the original blog post, use for comprehensions to manage the control flow of the program. For Comprehensions work by managing the iteration over any datastructure or type with flatMap and map methods (or equivalent).
(there is a Try)
For the synchronous example they wrap the return values from each method in scala.util.Try. Try represents one of two states either :-
The map and flatMap methods effect a transformation only if Try is in the Success state. If Try is in the Failure state, nothing happens. Try is ultimately an interface with Success and Failure implementations.
In practice, you write one path through your code, but if there is an error processing stops and the result will be a Failure instance that encapsulates the error. If there is no error, processing flows through the steps defined in the Comprehension and you get a Success instance with an actual result.
The Scala code looks like this.
In Java there are numerous implementations of Try including Javaslang Try and Cyclops Try. Javaslang Try is essentially a port of the Scala Try with the same rules and behaviours. That means that any errors during transformation actions such as map or flatMap are also captured and can convert a Success instance to a Failure. Cyclops Try differs in that it only captures exceptions that occur during the creation of Try object are caught, exceptions during transformation phases are thrown.
(back to the Futures)
By switching from using Try to CompletableFuture we can have the whole operation be executed asynchronously on separate threads, with results / data passed between threads where appropriate. Let’s take a look at how to transform the code.
The Java Code to implement this with Futures looks very similar to the code leveraging Try (note we can’t use them both together here, that would require the extra complexity of an implementation of appropriate Monad Transformers — eek!).
The original blog post recommends starting your Future tasks outside of the comprehension, and in Java we can do exactly the same thing.