32

我想Future[(Class1,Class2,Class3)]从下面的代码中创建一个 Future 类型。然而,我发现这样做的唯一方法是使用 zip()。我发现解决方案很丑陋,而且不是最佳的。任何人都可以启发我。

val v = for (
    a <- {
        val f0:Future[Class1] = process1
        val f1:Future[Class2] = process2
        val f2:Future[Class3] = process3
        f0.zip(f1).zip(f2).map(x => (x._1._1,x._1._2,x._2))
    } yield a  // Future[(Class1,Class2,Class3)]

我也尝试过使用Future.sequence(List(f0, f1, f2)),但这不起作用,因为新的 Future 将具有Future[List[U]]where Uis the lub 的类型,Class1/2/3而我想要一个保留原始类型的 3 元组

4

4 回答 4

51
val result: Future[(Class1, Class2, Class3)] = for {
  _ <- Future.unit
  val f1 = process1
  val f2 = process2
  val f3 = process3
  v1 <- f1
  v2 <- f2
  v3 <- f3
} yield (v1, v2, v3)
于 2012-06-22T14:47:18.737 回答
35

Applicative Functors

What you are asking for is an applicative functor for a future. See scalaz Applicative Builder pattern. It should be rather trivial to roll your own on the back of zip

(f0 |@| f1 |@| f2)(g) //g is function (Class1, Class2, Class3) => Z

This is equivalent to the direct applicative:

(f0 <***> (f1, f2))(g)

Scalaz ships with a banana braces method which forms a tuple from the target and the arguments (i.e. what you asked for). So your solution will be:

f0 <|**|> (f1, f2) //that. is. all.

You get all this simply by defining a typeclass instance for the following typeclass:

trait Apply[Z[_]] {
  def apply[A, B](f: Z[A => B], a: Z[A]): Z[B]
}

So for future this looks like:

implicit val FutureApply = new Apply[Future] {
  def apply[A, B](f: Future[A => B], a: Future[A]): Future[B] = 
    (f zip a) map { case (fn, a1) => fn(a1) }
  }
}

(Actually you'd need Pure and Functor as well. Might as well implement Bind whilst you're at it - see appendix)

The great thing about this pattern is that you will start to see it everywhere (e.g. in Option, in Validation, in List etc). For example, the cartesian product of 2 streams is:

s1 <|*|> s2

Notes

All the above assuming scalaz 6, doubtless scalaz 7 for 2.10 will ship with these typeclasses by default. Pure has been renamed Pointed in scalaz7.


Appendix

Other type class instances for future:

implicit val FuturePure = new Pure[Future] {
  def pure[A](a: =>A): Future[A] = Future { a }
}
implicit val FutureBind = new Bind[Future] {
  def bind[A, B](a: Future[A], f: A => Future[B]): Future[B] = a flatMap f
}
implicit val FutureFunctor = new Functor[Future] {
  def map[A, B](a: Future[A], f: A => B): Future[B] = a map f
}
于 2012-06-22T14:11:47.710 回答
3

如果您使用 akka,请查看数据流:http ://doc.akka.io/docs/akka/2.0.2/scala/dataflow.html

您需要使用 Delimited Continuations 插件(但使用 sbt 很容易),然后是:

val f:Future[(Class1,Class2,Class3)] = flow {
  val f0 = process1
  val f1 = process2
  val f2 = process3
  (f0(), f1(), f2())
}

应该编译。

在 build.sbt 中:

autoCompilerPlugins := true

addCompilerPlugin("org.scala-lang.plugins" % "continuations" % "2.9.1")
于 2012-06-22T14:26:20.503 回答
1

你也可以使用猫:

import cats._
import cats.instances.future._

有一些有用的方法可以做到这一点:

第一个更通用的选项:

Applicative[Future].map3(f0, f1, f2){ 
  (f0r, f1r, f2r) => //do something with results
}

更简单:) 只会返回元组 Future[(f0.type, f1.type, f2.type)

Applicative[Future].tuple3(f0, f1, f2)
于 2018-04-10T18:53:28.277 回答