2016-07-06 71 views
13

我怎么能转换Option[Future[T]]Future[Option[T]]在Scala呢?斯卡拉选项[未来[T]给未来的[选项[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]) 
+0

所以'一个< - addressDAO.insert(ca.address)'返回一个'未来[选项]'? –

+0

你能指定你的方法的签名吗? –

+5

我会用'.sequence'从'与https://github.com/milessabin/si2712fix-plugin scalaz/cats' - 但是这很可能是矫枉过正。 – Reactormonk

回答

18
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]] = [email protected] 
3

标准库确实提供了在选项上使用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

这里是另一种解决方案:

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中提取值。