This is the third of several posts describing the evolution of scala.concurrent.Future
in Scala 2.12.x
.
For the previous post, click here.
Missing canonical combinators: transform
Now, if you’re thinking “Hey Viktor, there’s already a transform
-method on Future
!” then you’re most definitely right. And I’ll explain myself.
When the old transform
1 was added, many moons ago, it was a means of mapping over successes and failures. However, the astute reader might notice that it is asymmetric: an exception thrown when mapping over the successful case will result in a failed Future
, but there is no way to turn a failed case into a successful one.
One way of looking at a scala.concurrent.Future
is that it’s an “Eventually[Try[_]]
”, in other words, a container which will eventually contain a scala.util.Try
parameterized by some type.
What if we had a way to transform Future
more generically, and symmetrically?
Perhaps its signature ought to look something like this:
def transform[S](f: Try[T] => Try[S])(implicit executor: ExecutionContext): Future[S]
So for instance, that would mean that Future.map
could be implemented as follows (and is!):
def map[S](f: T => S)(implicit executor: ExecutionContext): Future[S] = transform(_.map(f))
And Future.recover
is possible to implement as this (and is!):
def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Future[U] =
transform { _ recover pf }
It also makes it dead simple to lift a Future[T]
to a Future[Try[T]]
:
val someFuture: Future[String] = …
val lifted/*: Future[Try[String]]*/ = someFuture.transform(Try(_))
Nice, right?
Benefits:
- Strictly more powerful / generic than the previous
transform
-method - Straightforward to consume/transform a
Future
sTry[_]
without having to useonComplete
or unboxingFuture.value
- For implementors of the
Future
trait, less methods to implement
Click here for the next part in this blog series.
Cheers,
√
1:
def transform[S](s: T => S, f: Throwable => Throwable)(implicit executor: ExecutionContext): Future[S]