Опция Scala [Будущее [T]] в Будущее [Опция [T]]

Как я могу конвертировать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])
 airudah06 июл. 2016 г., 16:19
Такa <- addressDAO.insert(ca.address) возвращаетFuture[Option] ?
 Sergey Lagutin06 июл. 2016 г., 16:20
Не могли бы вы указать подписи ваших методов?
 Reactormonk06 июл. 2016 г., 16:21
Я бы использовал.sequence отscalaz/cats сgithub.com/milessabin/si2712fix-plugin - но это, вероятно, было бы излишним.

Ответы на вопрос(6)

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)

Вот еще одно решение:

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.

Если у вас есть кошки в качестве зависимости в вашем приложении, самый красивый способ будет использовать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)
Решение Вопроса
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]

Стандартная библиотека предоставляет методы для использования Future.sequence в Option, к сожалению, вы должны соединить их вместе.

Либо как быстрый метод:

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

Когда у вас есть список (или любойTraversableOnce) будущего и хотите одного будущего для вычисления всего списка, вы используетеFuture.sequence или жеFuture.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

Ваш ответ на вопрос