Map a Future for both Success and Failure

28,890

Solution 1

Edit 2017-09-18: As of Scala 2.12, there is a transform method that takes a Try[T] => Try[S]. So you can write

val future = ... // Future[T]
val mapped = future.transform {
  case Success(_) => Success("OK")
  case Failure(_) => Success("KO")
}

For 2.11.x, the below still applies:

AFAIK, you can't do this directly with a single PF. And transform transforms Throwable => Throwable, so that won't help you either. The closest you can get out of the box:

val mapped: Future[String] = future.map(_ => "OK").recover{case _ => "KO"}

That said, implementing your mapAll is trivial:

implicit class RichFuture[T](f: Future[T]) {
  def mapAll[U](pf: PartialFunction[Try[T], U]): Future[U] = {
    val p = Promise[U]()
    f.onComplete(r => p.complete(Try(pf(r))))
    p.future
  }
}

Solution 2

Since Scala 2.12 you can use transform to map both cases:

future.transform {
      case Success(_) => Try("OK")
      case Failure(_) => Try("KO")
}

You also have transformWith if you prefer to use a Future instead of a Try. Check the documentation for details.

Solution 3

Both map and flatMap variants:

implicit class FutureExtensions[T](f: Future[T]) {
  def mapAll[Target](m: Try[T] => Target)(implicit ec: ExecutionContext): Future[Target] = {
    val promise = Promise[Target]()
    f.onComplete { r => promise success m(r) }(ec)
    promise.future
  }

  def flatMapAll[Target](m: Try[T] => Future[Target])(implicit ec: ExecutionContext): Future[Target] = {
    val promise = Promise[Target]()
    f.onComplete { r => m(r).onComplete { z => promise complete z }(ec) }(ec)
    promise.future
  }
}

Solution 4

In a first step, you could do something like:

import scala.util.{Try,Success,Failure}

val g = future.map( Success(_):Try[T] ).recover{
  case t => Failure(t)
}.map {
  case Success(s) => ...
  case Failure(t) => ...
}

where T is the type of the future result. Then you can use an implicit conversion to add this structure the Future trait as a new method:

implicit class MyRichFuture[T]( fut: Future[T] ) {
  def mapAll[U]( f: PartialFunction[Try[T],U] )( implicit ec: ExecutionContext ): Future[U] = 
    fut.map( Success(_):Try[T] ).recover{
      case t => Failure(t)
    }.map( f )
 }

which implements the syntax your are looking for:

val future = Future{ 2 / 0 }
future.mapAll {
  case Success(i) => i + 0.5
  case Failure(_) => 0.0
}
Share:
28,890
sksamuel
Author by

sksamuel

Building systems with lots of love, drippings of chocolate, lashings of Java and sprinkles of Scala.

Updated on July 11, 2022

Comments

  • sksamuel
    sksamuel almost 2 years

    I have a Future[T] and I want to map the result, on both success and failure.

    Eg, something like

    val future = ... // Future[T]
    val mapped = future.mapAll { 
      case Success(a) => "OK"
      case Failure(e) => "KO"
    }
    

    If I use map or flatmap, it will only map successes futures. If I use recover, it will only map failed futures. onComplete executes a callback but does not return a modified future. Transform will work, but takes 2 functions rather than a partial function, so is a bit uglier.

    I know I could make a new Promise, and complete that with onComplete or onSuccess/onFailure, but I was hoping there was something I was missing that would allow me to do the above with a single PF.