在理解中混合期货和向量

Mixing futures and vectors in a for comprehension

我想迭代从未来获得的向量。我希望下面的工作正常,但它在 user <- usersToReview 行给出了类型不匹配的编译器错误。

import scala.concurrent.{Await, Future}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.duration._

object Main3 {
  var counter = 0

  val fUsersToReview: Future[Vector[String]] = Future {Vector("u1", "u2", "u3")}

  def doIt(user: String): Future[Int] = {
    counter = counter + 1
    Future.successful(counter)
  }

  def main(args: Array[String]): Unit = {
    val result:Future[Vector[Int]] = for {
      usersToReview ← fUsersToReview
      user ← usersToReview
      msg_id ← doIt(user)
    } yield {
      msg_id
    }

    println(Await.result(result, 1.second))
  }
}

下面的作品,但看起来很笨拙。有关如何改进此问题的建议?

  def main(args: Array[String]): Unit = {
    val result:Future[Vector[Int]] = (for {
      usersToReview ← fUsersToReview
    } yield {

      Future.sequence(
        for (u ← usersToReview) yield {
          doIt(u)
        }
      )
    }).flatMap(identity)

    println(Await.result(result, 1.second))
  }
}

这是一个不使用 for-comprehension 而只使用 map:

可能更干净的例子
val result: Future[Vector[Int]] = fUsersToReview.map(_.map(doIt(_)))

对于 OptionEither 这样的东西,你可以使用像 OptionT and EitherT 这样的 monad 转换器,但是 Vector.

没有任何东西

多亏了adrice727的回答的思考,我找到了解决问题的办法。它涉及重构 doIt 以获取向量。 通过将向量的处理转移到 doIt,向量从 for-comprehension 中出来。一旦它只处理期货,它就会按预期工作。

我已经稍微简化了我原来的 post,所以下面的内容在 for-comprehension 中增加了一个级别,并且 doIt 实际上对它传递的数据做了一些事情。其背景是fMaintainersfUsersToReviewdoIt都是作用于一个数据库。

val fMaintainers: Future[Vector[String]] = Future {Vector("m1", "m2", "m3")}

val fUsersToReview: Future[Vector[String]] = Future {Vector("u1", "u2", "u3")}

def doIt(users: Vector[String], maintainers: Vector[String]): Future[Vector[(String, String)]] = {
  Future.sequence(for (u ← users) yield {
    Future.successful((u, maintainers.head))
  })
}

val result: Future[Vector[(String, String)]] = for {
  maintainers ← fMaintainers
  usersToReview ← fUsersToReview
  msg_id ← doIt(usersToReview, maintainers)
} yield {
  msg_id
}

println(Await.result(result, 1.second))

最后,跟进 adrice727 的建议,即有时 for-comprehensions 不是正确的工具,这里是地图:

val result:Future[Vector[(String, String)]] = 
      fMaintainers.flatMap { m ⇒ fUsersToReview.flatMap { u ⇒ doIt(u, m) } }