Scala Option [Future [T]] to Future [Option [T]]

时间:2016-07-06 14:12:31

标签: scala future

如何在scala中将Option[Future[T]]转换为Future[Option[T]]

我想在:

中使用它
val customerAddresses = for {
  a <- addressDAO.insert(ca.address) // Future[Address]
  ia <- ca.invoiceAddress.map(addressDAO.insert) // Option[Future[Address]]
} yield (a, ia) // Invalid value have to be two futures

这里签名插入方法

def insert(address: Address): Future[Address]

ca是CustomerData

case class CustomerData(address: Address, invoiceAddress: Option[Address])

7 个答案:

答案 0 :(得分:26)

import scala.concurrent.Future
import scala.concurrent.ExecutionContext

def f[A](x: Option[Future[A]])(implicit ec: ExecutionContext): Future[Option[A]] = 
  x match {
     case Some(f) => f.map(Some(_))
     case None    => Future.successful(None)
  }

示例:

scala> f[Int](Some(Future.successful(42)))
res3: scala.concurrent.Future[Option[Int]] = Success(Some(42))

scala> f[Int](None)
res4: scala.concurrent.Future[Option[Int]] = scala.concurrent.impl.Promise$KeptPromise@c88a337

答案 1 :(得分:7)

标准库确实提供了在Option上使用Future.sequence的方法,遗憾的是你必须将它们连接在一起。

作为一种快速方法:

def swap[M](x: Option[Future[M]]): Future[Option[M]] =
    Future.sequence(Option.option2Iterable(x)).map(_.headOption)

注意我发现隐含的Option.option2Iterable已经在我的范围内了。因此,您可能不需要提供它,将代码减少到Future.sequence(x).map(_.headOption)

或者您可能更喜欢扩展方法:

implicit class OptionSwitch[A](f: Option[Future[A]]) {
    import scala.concurrent.Future

    def switch: Future[Option[A]] = Future.sequence(Option.option2Iterable(f))
      .map(_.headOption)
  }


val myOpt = Option(Future(3))
myOpt.switch

答案 2 :(得分:3)

这是另一种解决方案:

def swap[T](o: Option[Future[T]]): Future[Option[T]] =
  o.map(_.map(Some(_))).getOrElse(Future.successful(None))

诀窍是将Option[Future[T]]转换为Option[Future[Option[T]]]这很容易,然后从Option中提取值。

答案 3 :(得分:1)

如果您的应用程序中有猫作为依赖项,那么最漂亮的方法是使用traverse

import cats._
import cats.implicits._

val customerAddresses = for {
  a <- addressDAO.insert(ca.address) // Future[Address]
  ia <- ca.invoiceAddress.traverse(addressDAO.insert) // Future[Option[Address]]
} yield (a, ia)

答案 4 :(得分:1)

当您拥有一个期货列表(或任何TraversableOnce)并且想要一个期货来计算整个列表时,请使用Future.sequenceFuture.traverse。您可以将Option视为一个包含1个或0个元素的列表,但是从技术上讲,由于它不是一个列表,因此在这种情况下您必须进行一些转换。无论如何,这是正常执行的代码:

  val optionFuture:Option[Future[String]] = ???

  val futureOption:Future[Option[String]] = Future.sequence(optionFuture.toIterable).map(_.headOption)

在您的示例中,使用更好的Future.traverse

  val customerAddresses = for {
    a <- addressDAO.insert(ca.address) // Future[Address]
    ia <- Future.traverse(ca.invoiceAddress.toIterable)(addressDAO.insert).map(_.headOption) // Future[Option[Address]]
  } yield CustomerData(a, ia) // Types OK

答案 5 :(得分:0)

val customerAddresses = for {
  a <- addressDAO.insert(ca.address) // Future[Address]
  ia <- ca.invoiceAddress.map(x => addressDAO.insert(x).map(_.map(k => Some(k))).getOrElse(Future.successful(None)))
} yield (a, ia)

答案 6 :(得分:0)

您只需使用 Dumonad

import io.github.dumonad.dumonad.Implicits._
futureOfOption.dummed