Scala Futures callback hell

I read many times about Scala Futures, reducing callback problems. I have code that has become problematic.

val a = Future(Option(Future(Option(10)))) a.map { b => b.map { c => c.map { d => d.map { res => res + 10 } } } } 

How can I make this code flatter?

// Edit @againstmethod

 for{ b <- a c <- b d <- c res <- d } yield res + 10 

This code will not compile

Error: (21, 8) type mismatch; found: option [Int] required:
scala.concurrent.Future [?] res <-d
^

+7
callback scala future
source share
3 answers

Actually the answer was pretty straightforward.

 for { a <- b c <- a.get } yield c.get + 10 

Appears enough because when x.get + 10 fails (due to None + 10 ), the future just fails. So it still works to use a simple backup

 val f = for { a <- b c <- a.get } yield c.get + 10 f fallbackTo Future.successful(0) 
+1
source share

You can use for understanding . In the example:

 import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global object Stuff extends App { val result = for { f1 <- Future { 10 + 1 } f2 <- Future { f1 + 2 } } yield f2 result.onComplete(println) } 

If the result is 13.

Any class that implements the correct map and flatMap functions can be used this way in for .

If you don't mind another dependency, you can also use a library such as scalaz, and explicitly use monadic binding to align things (EDIT encoded some types of options to respond to the comment below):

 import scalaz._ import Scalaz._ import scala.concurrent._ import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ import scala.util.{Success,Failure} object BindEx extends App { def f1(i: String): Future[Int] = Future { i.length } def f2(i: Int): Future[Option[Double]] = Future { Some(i / Math.PI) } def f3(i: Option[Double]): Future[Option[Double]] = Future { i match { case Some(v) => Some(Math.round(v)) case _ => None } } val result = Monad[Future].point("Starting Point") >>= f1 >>= f2 >>= f3 result.onComplete { x => x match { case Success(value) => println("Success " + value) case Failure(ex) => println(ex) } } Await.result(result, 1 seconds) } 

And finally, if you only have parallel operations that you want to bind, after all of them were independent, you can use the scalaz applicative builder:

  val result = ( Future { 10 + 10 } |@| Future { 20 - 3 } |@| Future { Math.PI * 15 } ) { _ + _ / _} println(Await.result(result, 1 seconds)) 

This will complete all 3 futures, and then apply the block to 3 arguments.

+2
source share

I have not used them yet, but they should be exactly what you are looking for: Monad transformers.

Basically, a monad transformer accepts Monad (e.g. Future) and adds functionality to it, such as the functionality provided by Option, and returns the converted Monad. I think Scalaz even has an Option Monad transformer. This should allow you to use the nested options in Futures and still have a flat code structure used for understanding.

See http://blog.garillot.net/post/91731853561/a-question-about-the-option-monad-transformer and https://softwarecorner.wordpress.com/2013/12/06/scalaz-optiont- monad-transformer / for some examples.

0
source share

All Articles