J. Doe
J. Doe

Reputation: 23

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?

Upvotes: 2

Views: 5210

Answers (3)

adamwy
adamwy

Reputation: 1239

Scala Futures are evaluated eagerly, which means that their value is immediately calculated in a separate thread.

When you run operations that depend on result of the future, the current thread is going to block waiting until it becomes evaluated. If you transform Futures using combinator methods such as map or flatMap, you are getting another Future that represents the end result (the code running these operations doesn't need to block).

So in your example the for comprehension is desugared by the compiler to the following expression:

fFuture.flatMap(f => gFuture.map(g => f + g))

Which itself is a Future[Int] calculated in another thread.

Upvotes: 1

dk14
dk14

Reputation: 22374

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

Upvotes: 5

user2056463
user2056463

Reputation: 143

Here in your scenario, you dont have to use for comprehension, you can simply use OnComplete.

ffuture.OnComplete {
Case Success(result) => println(s"$result')
Case Failure(ex) => ex.printStackTrace
}

For Comprehension is needed when you have future(s) dependent on other future(s) like:

var result =  for {
        f <- fFuture
        g <- f
    } yield g

Here g future will be resolved after f completes, and yield will return whatever is the result you are returning. In this case it will be a Future[Int]. With yield you can do things like.

yield g+f

To read the result you can simply use

result.onSuccess or onComplete

For Futures, I found this article as the best one: http://alvinalexander.com/scala/concurrency-with-scala-futures-tutorials-examples

I will say yes, this is similar to return value in a function. But this is for comprehension syntax, you cant use return statement with it. For comprehension is a better way to write map.

Upvotes: -2

Related Questions