Scala - Chaining Futures Try blocks?

10,766

Solution 1

When faced with a problem like this, where you want to use differing types in a for comprehension, one solution can be to try and select one of the types and map the other type to it. For your situation, given the unique properties (async) of futures, I would select Future as the lowest common denominator and map the Try to the Future. You can simply do that like this:

val result = for{
  a <- someFuture
  b <- tryToFuture(processResult(a)) 
}  yield b
result.map { /* Success Block */ } recover { /* Failure Block */ }

def tryToFuture[T](t:Try[T]):Future[T] = {
  t match{
    case Success(s) => Future.successful(s)
    case Failure(ex) => Future.failed(ex)
  }
}

Now if you found this to be a very common situation and you did not like constantly having to add in the explicit conversion, I suppose you could define the tryToFuture method as implicit on some helper object and import it where needed like this:

object FutureHelpers{
  implicit def tryToFuture[T](t:Try[T]):Future[T] = {
    t match{
      case Success(s) => Future.successful(s)
      case Failure(ex) => Future.failed(ex)
    }
  }
}

import FutureHelpers._
val result = for{
  a <- someFuture
  b <- processResult(a) 
}  yield b
result.map { /* Success Block */ } recover { /* Failure Block */ }

Just remember that calling Future.success and Future.failed has an impact on whatever ExecutionContext is in scope in that it will submit another task to it under the hood.

EDIT

As Viktor pointed out in the comments, the process of converting a Try to aFuture is even easier if you just use Future.fromTry like in the updated example from below:

val result = for{
  a <- someFuture
  b <- Future.fromTry(processResult(a)) 
}  yield b
result.map { /* Success Block */ } recover { /* Failure Block */ }

This is probably your best bet versus doing stuff with implicits or rolling your own conversion logic.

Solution 2

Perhaps the problem is old but currently you can:

  implicit def tryToFuture[T](t:Try[T]):Future[T] = Promise.fromTry(t).future

Solution 3

How about

val result = for( a <- someFuture) yield for( b <- processResult( a ) ) yield b;

Although it does not look neat.

Solution 4

  implicit def convFuture[T](ft: Future[Try[T]]): Future[T] =
ft.flatMap {
  _ match {
    case Success(s) => Future.successful(s)
    case Failure(f) => Future.failed(f)
  }
}

Solution 5

There is also

Future.fromTry(Try { ... })

So you could do

 val result = for {
   a <- someFuture
   b <- Future.fromTry(processResult(a)) 
 } yield b;
Share:
10,766

Related videos on Youtube

James Davies
Author by

James Davies

Updated on June 07, 2022

Comments

  • James Davies
    James Davies almost 2 years

    Is it possible to chain scala.util.Try and scala.concurrent.Future? They both effectively provide the same monadic interface, but attempting to chain them results in a compile error.

    For example. Given the two signatures below

    def someFuture:Future[String] = ???
    def processResult(value:String):Try[String] = ???
    

    is it possible to do something like the following?

    val result = for( a <- someFuture; b <- processResult( a ) ) yield b;
    result.map { /* Success Block */ } recover { /* Failure Block */ }
    

    This obviously results in a compile error, because Future and Try are unable to be flatMapp'ed together.

    It would however be a nice feature to be able to chain them - is this at all possible? Or do I need to combine them into a Future[Try[String]]?

    (In particular, I'm interested in having a single 'recover' block to catch exceptions in either the future or the try).

  • gourlaysama
    gourlaysama over 10 years
    there is currently a discussion on scala-user about this exact problem of converting a Try to a Future. Maybe an helper like this should be included in the standard library somewhere.
  • cmbaxter
    cmbaxter over 7 years
    @ViktorKlang, yes you are right. I updated my answer to include that approach. Thanks for the heads up.