0

In the following two functions, someF1 will not compile but someF2 will. It's because someF1 is expected to return Future[Int] but it returns Future[Future[Int]] because I am using a for within a for. I don't face issue in someF2 because I am flattening Future[Future[]] in someF2. Is there an equivalent way of flattening results if I use embedded for or is someF1 a wrong use case of for and I should use it only to execute Futures in parallel and not in sequence?

  // Start writing your ScalaFiddle code here
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

def someF1:Future[Int] = {
  val f1 = Future[Int]{1}

  for(i<-f1) yield{
    if(i == 0) {
      val f2 = Future{2}
      for(j<-f2) yield {i+j} //this will make function return Future[Future[Int]]
    }
    else {
      val f3 = Future{3}
      for(k<-f3) yield {i+k}//this will make function return Future[Future[Int]]
    }
  }
}

// Start writing your ScalaFiddle code here
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

def someF2:Future[Int] = {
  val f1 = Future[Int]{1}

  f1.flatMap{i=> //flatMap will flatten Future[Future[Int]]
    if(i == 0) {
      val f2 = Future{2}
      f2.map(j=> {i+j})
    }
    else {
      val f3 = Future{3}
      f3.map(k=>{i+k})
    }
  }
}
jwvh
  • 50,871
  • 7
  • 38
  • 64
Manu Chadha
  • 15,555
  • 19
  • 91
  • 184

2 Answers2

2

The rule is that, with a for comprehension, the final generator (i.e. <-) is translated into a map() call. Every other generator is a flatMap()call. So, no, you can't achieve what you're after using nested for. That creates too many map() calls and not enough flatMap() calls.

This will work:

def someF1:Future[Int] = {
  val f1 = Future[Int]{1}

  for {
    i <- f1
    j <- Future(2)
    k <- Future(3)
  } yield {
    if (i == 0) i+j
    else        i+k
  }
}

But in that case you are launching one more Future than you actually need.

jwvh
  • 50,871
  • 7
  • 38
  • 64
  • well explained. Thanks – Manu Chadha Sep 18 '18 at 06:34
  • could you please look at https://stackoverflow.com/questions/52373295/unable-to-use-for-comprehension-to-resolve-future. This is my main issue. Basically, I am doing several database queries each of which return a Future and whether to do the next database query depends on the result of previous one. I am able to code it using `flatMap` and `map` but not with `for`. – Manu Chadha Sep 18 '18 at 06:42
1

Complementing @jwvh's answer

You can still avoid the sencond future evaluation with the following, more complicated, piece of code:

def someF1:Future[Int] =
  for {
    i <- Future[Int]{1}
    r <- if (i == 0) {
        for (j <- Future[Int]{2}) yield i + j
      } else {
        for (k <- Future[Int]{3}) yield i + k
      }
  } yield r

Also, I would recommend you to take a look at the documentation of for/yield

  • I'll look at this. Maybe this is what I need for the issue I am trying to resolve - https://stackoverflow.com/questions/52373295/unable-to-use-for-comprehension-to-resolve-future – Manu Chadha Sep 18 '18 at 06:43