Update: You can actually get the nice .sequence
syntax in Scalaz 7 without too much fuss:
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{ Future, future }
import scalaz._, Scalaz.{ ToTraverseOps => _, _ }
import scalaz.contrib.std._
val m = Map("a" -> future(1), "b" -> future(2), "c" -> future(3))
And then:
scala> m.sequence.onSuccess { case result => println(result) }
Map(a -> 1, b -> 2, c -> 3)
In principle it shouldn't be necessary to hide ToTraverseOps
like this, but for now it does the trick. See the rest of my answer below for more details about the Traverse
type class, dependencies, etc.
As copumpkin notes in a comment above, Scalaz contains a Traverse
type class with an instance for Map[A, _]
that is one of the puzzle pieces here. The other piece is the Applicative
instance for Future
, which isn't in Scalaz 7 (which is still cross-built against pre-Future
2.9), but is in scalaz-contrib
.
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scalaz._, Scalaz._
import scalaz.contrib.std._
def sequence[A, B](m: Map[A, Future[B]]): Future[Map[A, B]] = {
type M[X] = Map[A, X]
(m: M[Future[B]]).sequence
}
Or:
def sequence[A, B](m: Map[A, Future[B]]): Future[Map[A, B]] =
Traverse[({ type L[X] = Map[A, X] })#L] sequence m
Or:
def sequence[A, B](m: Map[A, Future[B]]): Future[Map[A, B]] =
TraverseOpsUnapply(m).sequence
In a perfect world you'd be able to write m.sequence
, but the TraverseOps
machinery that should make this syntax possible isn't currently able to tell how to go from a particular Map
instance to the appropriate Traverse
instance.
Traversable
(the Haskell sense of the word) instance forMap
, which would do exactly what you need. – CognomenTraverse
—see my answer below for how you'd use it here. – Bawdy