Scala Option[Future[T]] to Future[Option[T]]
Solution 1
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)
}
Examples:
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
Solution 2
If you have cats as a dependency in your application, the most beautiful way would be to use 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)
Solution 3
The standard library does provide the methods to use Future.sequence on an Option, unfortunately you have to plumb them together.
Either as a quick method:
def swap[M](x: Option[Future[M]]): Future[Option[M]] =
Future.sequence(Option.option2Iterable(x)).map(_.headOption)
Note I found the implicit Option.option2Iterable
was already in scope for me. So you may not need to provide it, reducing the code down to Future.sequence(x).map(_.headOption)
Or you may prefer an extension method:
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
Solution 4
Here is another solution:
def swap[T](o: Option[Future[T]]): Future[Option[T]] =
o.map(_.map(Some(_))).getOrElse(Future.successful(None))
The trick is to convert Option[Future[T]]
into Option[Future[Option[T]]]
which is easy, and then extract the value from that Option
.
Solution 5
When you have a list (or any TraversableOnce
) of futures and want a single future for computing the whole list, you use Future.sequence
or Future.traverse
. You can think of an Option like a list of 1 or 0 elements but since is technically not a list you have to go for a little conversion in this case. Anyway, this is a code that does it normally:
val optionFuture:Option[Future[String]] = ???
val futureOption:Future[Option[String]] = Future.sequence(optionFuture.toIterable).map(_.headOption)
In you example use better 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
Michał Jurczuk
Updated on June 05, 2022Comments
-
Michał Jurczuk almost 2 years
How can I convert
Option[Future[T]]
toFuture[Option[T]]
in scala?I want to use it 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
Here signature insert method
def insert(address: Address): Future[Address]
ca
is a CustomerDatacase class CustomerData(address: Address, invoiceAddress: Option[Address])