This is a follow-up to Retry in Java futures. This post presented a snippet of code demonstrating how to compose futures in Java, specifically
java.util.concurrent.CompletableFuture. It demonstrated how to swallow exceptions and compose futures including branching etc.
It turns out that I need to interoperate with Guava futures too, specifically
com.google.common.util.concurrent.ListenableFuture. In some ways this post can be considered to be a short translation guide for switching back and forth between
On first encountering
ListenableFuture I was dismayed that, unlike
CompletableFuture, this interface does not expose any methods for composing futures.
CompletableFuture, for example, provides the following and many more:
On deeper inspection, however, we find that Guava eschews interface methods for static helper methods on the
com.google.common.util.concurrent.Futures class. Roughly corresponding to the methods mentioned above we find:
My “add-with-retry” example using
CompletableFuture looks like:
These are my observations about this:
.exceptionally(e -> null)
nullreturned from the
App.addfuture and a swallowed exception
Regarding the union type comment above, I can think of at least one more principled approach we might take to handling exceptions that does not lose information. We could, for example, introduce a tagged union type as follows:
With the introduction of the tagged union,
ValueOrException (roughly equivalent to Haskell’s
Either), we can now distinguish accurately between the exceptional case and the
null response case.
Here’s my rough translation of this example into Guava futures:
And here’s the accompanying
ListenableFutureHelper helper class:
Here are my thoughts:
Futuresas opposed to first-class members of the
catchingAsyncis far superior to
thenComposeand does not lose information and does not require the invention of a tagged union to distinguish between
In order to address the syntactic issues with
ListenableFuture, Guava now has
com.google.common.util.concurrent.FluentFuture in recent versions of the library. I haven’t had a chance to play with this yet. If I do, I’ll get back to you!
The latest and greatest version of this code can be found in the GitHub project.
Update: Here’s the
Update 2: Cancellation
You’ll notice that I sneakily introduced early cancellation into the
FluentFuture example. If you want to see how I added support for proper cancellation using all three future APIs, please consult the GitHub project.
Content © 2023 Richard Cook. All rights reserved.