大隻雞慢啼

大隻雞慢啼

我們是小國小民,但是我們是好國好民。 - 鄭南榕

15 Mar 2017

Future.zip versus Future.sequence in Scala

Future.sequence is a handy method to deal with a sequence futures of the same type. However, if you want to combine futures of different types, you should use Future.zip to avoid losing type information.

Let’s take a look at an example:

scala> import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.ExecutionContext.Implicits.global

scala> val f1 = Future.successful(55)
f1: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$KeptPromise@57855c9a

scala> val f2 = Future.successful(Six Six)
f2: scala.concurrent.Future[String] = scala.concurrent.impl.Promise$KeptPromise@475530b9

// note the type is Future[Seq[Any]] because `55` and `Six Six` are of different types
scala> val fSeq = Future.sequence(Seq(f1, f2))
fSeq: scala.concurrent.Future[Seq[Any]] = scala.concurrent.impl.Promise$DefaultPromise@51acdf2e

// Therefore, you have to explicitly cast types (a bad idea)
scala> fSeq.map { case (num :: str :: rest) => println(s”${num.asInstanceOf[Int].toBinaryString} ${str.asInstanceOf[String].reverse}) }
res2: scala.concurrent.Future[Unit] = scala.concurrent.impl.Promise$DefaultPromise@18da4dd
110111 xiS xiS

// note the type information is retained: Future[(Int, String)]
scala> val zipFuture = f1 zip f2
zipFuture: scala.concurrent.Future[(Int, String)] = scala.concurrent.impl.Promise$DefaultPromise@5ab14cb9

// No need to cast type
scala> zipFuture.map{ case (num, str) => println(s"${num.toBinaryString} ${str.reverse}") }
res1: scala.concurrent.Future[Unit] = scala.concurrent.impl.Promise$DefaultPromise@16c8b7bd
110111 xiS xiS