Kevin Meredith
Kevin Meredith

Reputation: 41909

Option[Future[Option[Int]]] => Future[Option[Int]]

Given a Option[Future[Option[Int]]]:

scala> val x: Option[Future[Option[Int]]] = Some ( Future ( Some ( 10 ) ) )
x: Option[scala.concurrent.Future[Option[Int]]] = 
    Some(scala.concurrent.impl.Promise$DefaultPromise@446a1e84)

I want Future[Option[Int]].

I can pattern match (or use Option#getOrElse):

scala> x match { 
     |   case Some(f) => f
     |   case None    => Future { None } 
     | }
res6: scala.concurrent.Future[Option[Int]] =  
     scala.concurrent.impl.Promise$DefaultPromise@446a1e84

scala> res6.value
res7: Option[scala.util.Try[Option[Int]]] = Some(Success(Some(10)))

But, is there a higher-order function that will do the job?

I thought of using sequence, but I don't have an outer type of List:

> :t sequence
sequence :: Monad m => [m a] -> m [a]

Upvotes: 5

Views: 1407

Answers (1)

Travis Brown
Travis Brown

Reputation: 139038

Haskell's sequence isn't as generic as it could be, or as generic as Scalaz's (and I'm assuming you're okay with a Scalaz solution since you mention sequence).

Scalaz's sequence (and Haskell's sequenceA in Data.Traversable) only requires that the outer type constructor have a Traverse instance—it doesn't necessarily have to be a list. Option has a Traverse instance, so sequence will work just fine here:

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scalaz._, Scalaz._

def collapse(x: Option[Future[Option[Int]]]): Future[Option[Int]] =
  x.sequence.map(_.flatten)

Scalaz also provides an orZero extension method for Option, which would allow you just to write x.orZero, since the zero of Future[Option[Int]] is Future(None).

I'd actually probably use x.getOrElse(Future.successful(None)), though, in this case—it's slightly (probably irrelevantly) more performant, but more importantly it's as clear and almost as concise as the Scalaz options.

Upvotes: 8

Related Questions