2016-07-06 45 views
13

Come convertire Option[Future[T]] in Future[Option[T]] in scala?Opzione Scala [Futuro [T]] a Futuro [Opzione [T]]

voglio usarlo in:

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 

Qui firma del metodo di inserimento

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

ca è un CustomerData

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

Quindi 'a <- addressDAO.insert (ca.address)' restituisce un 'Future [Option]'? –

+0

Puoi specificare le firme dei tuoi metodi? –

+5

Userei '.sequence' da' scalaz/cats' con https://github.com/milessabin/si2712fix-plugin - ma probabilmente sarebbe eccessivo. – Reactormonk

risposta

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) 
    } 

Esempi:

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

La libreria standard fornisce i metodi per utilizzare Future.sequence su un'opzione, sfortunatamente è necessario analizzarli insieme.

Sia come un metodo rapido:

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

Nota ho trovato l'implicita Option.option2Iterable era già portata per me. Quindi potrebbe non essere necessario a fornirle, riducendo il codice fino a Future.sequence(x).map(_.headOption)

Oppure si può preferire un metodo di estensione:

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

Ecco un'altra soluzione:

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

Il trucco è quello di convertire Option[Future[T]] in Option[Future[Option[T]]] che è facile e quindi estrarre il valore da quello Option.