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).
onComplete() we are no longer blocking for the result from the Future but instead we will receive a callback for either a Success or a Failure. As such, we've also had to import scala. util.
By default, futures and promises are non-blocking, making use of callbacks instead of typical blocking operations. To simplify the use of callbacks both syntactically and conceptually, Scala provides combinators such as flatMap , foreach , and filter used to compose futures in a non-blocking way.
Promise is an object which can be completed with a value or failed with an exception. A promise should always eventually be completed, whether for success or failure, in order to avoid unintended resource retention for any associated Futures' callbacks or transformations. Source Promise.scala. AnyRef, Any. Promise.
Await. result tries to return the Future result as soon as possible and throws an exception if the Future fails with an exception while Await. ready returns the completed Future from which the result (Success or Failure) can safely be extracted.
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.
How about
val result = for( a <- someFuture) yield for( b <- processResult( a ) ) yield b;
Although it does not look neat.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With