Manu Chadha
Manu Chadha

Reputation: 16723

is there an equivalent of flattening return values if I use for comprehension within for comprehension

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})
    }
  }
}

Upvotes: 0

Views: 38

Answers (2)

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

Upvotes: 1

jwvh
jwvh

Reputation: 51271

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.

Upvotes: 2

Related Questions