Search code examples
scalafuturefor-comprehension

Scala Futures in For Comprehension


I'm trying to wrap my head around Scala and I'm wondering what the following does:

val fFuture: Future[Int] = Future { println("f called"); 3 }                                                                                                                    
val gFuture: Future[Int] = Future { println("g called"); 4 }                                                                                                                    

for {
    f <- fFuture
    g <- gFuture
} yield f  

The futures are executed inside of the for comprehension, correct? So what does f do in the yield statement? Does that just mean it's available? Is that considered the return value if this were inside of a function?


Solution

  • The futures are starting to execute here:

    val fFuture: Future[Int] = Future { println("f called"); 3 }                                                                                                                    
    val gFuture: Future[Int] = Future { println("g called"); 4 } 
    

    So both executions are starting in parallel. However if you accidentally put Future{...} inside for-comprehension - they gonna be executed sequentially.

    For-comprehension basically subscribes and merges two results into one Future. However, in your case it seems like second future's result was ignored, which doesn't makes sense. The code that makes sense:

    for {
        f <- fFuture
        g <- gFuture
    } yield f + g
    

    This code returns Future[Int](same as code in your example). If you extract value from this Future - you get 3 + 4 = 7. However it's still not a best approach as your computations are independent and probability of developer's mistake (stated above) that makes them sequential is still high, so recommended approach for independent computations is:

    (fFuture zip gFuture) map {
      case (f, g) => f + g
    }
    

    This code is referentially transparent in meaning that even if you replace fFuture with Future{...} - it still behaves same (in case of Future -they're going to be executed in prallel, but it might be different for other concurrency primitives)

    Where would for-comprehension actually make sense? Here:

    for {
        f <- Future{... 9}
        g <- if (f > 0) Future{...} else Future{...}
    } yield g
    

    As g depends on f here - there is no way to run those in parallel, so for provides a non-blocking way of composing several Futures