2016-07-06 69 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中提取值。