Casual Snippets insights on software development

Future.transform in Scala 2.12

Scala 2.12 added a few improvements to scala.concurrent.Future. Here I am going to take a quick look at the new transform and transformWith methods. First, let’s see the old method signature:

 def transform[S](s: (T) ⇒ S, f: (Throwable) ⇒ Throwable): Future[S]

Creates a new future by applying the ‘s’ function to the successful result of this future, or the ‘f’ function to the failed result.

The main shortcoming of this method is related to f. Even though we can handle the exception case, the signature Throwable ⇒ Throwable bounds the future to fail once an exception is thrown.

The new method is much more powerful in this regard:

 def transform[S](f: (Try[T]) ⇒ Try[S]): Future[S] 

Creates a new Future by applying the specified function to the result of this Future.

By providing a function Try[T] ⇒ Try[S] we can handle both Success and Failure cases and transform them in any way we like. For example, if we want a future to always complete successfully we can now write it like this:

  scala> val future = Future(throw new Exception("Oops"))

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

  res0: scala.concurrent.Future[String] = Future(Success(KO))

To achieve a similar behaviour in Scala 2.11 we would have to combine map and recover statements or use onComplete in a Promise.

The new transformWith unifies flatMap and recoverWith in a similar way. Here is the signature:

def transformWith[S](f: Try[T] => Future[S]): Future[S]

Creates a new Future by applying the specified function, which produces a Future, to the result of this Future.

If you want to transform your result and you need to return another Future instead of a Try, go for transformWith.

Make sure to check the blog posts from Viktor Klang for more information about Futures on Scala 2.12 and, as always, check the documentation for details.