Why does a for-comp inside of a for-comp not work

2019-08-12 08:36发布

问题:

The following works fine:

def show(id: Int) = myAction asyc {

  for {
    users <- userService.getAll()
    locations <- locationService.getByUsers(users)
  } yield {
    Ok("hi")
  }

}

But if I do this:

 for {
    users <- userService.getAll()
    locations <- locationService.getByUsers(users)
  } yield {
    for {
      f1 <- ...
      f2 <- ....
    } yield {
      Ok("hi")
    }
  }

I get a type mismatch error:

found   : scala.concurrent.Future[play.api.mvc.Result]
[error]  required: play.api.mvc.Result
[error]           f1 <- ....

Hoping someone can explain this for me.

回答1:

Adding type annotations might help make clearer what's going on:

val result: Future[Future[Result]] = for {
  users <- userService.getAll()
  locations <- locationService.getByUsers(users)
} yield {
  val innerResult: Future[Result] = for {
    f1 <- ...
    f2 <- ....
  } yield {
    Ok("hi")
  }
  innerResult
}

By yielding a Future[Result] (innerResult), you're causing the outermost for to be of type Future[Future[Result]].

We know that result is going to have to be of type Future[???] (determined by the type of the first generator, userService.getAll()). It winds up being a Future[Future[Result]] because you yield a Future[Result].

The solution is to yield a Result intead:

def foo(users: Seq[User], locations: Seq[Location]): Future[Result] = {
  for {
    f1 <- ...
    f2 <- ....
  } yield {
    Ok("hi")
  }
}

for {
  users <- userService.getAll()
  locations <- locationService.getByUsers(users)
  result <- foo(users, locations)
} yield result