await future scala
Cómo combinar Futuros de diferentes tipos en un solo Futuro sin usar zip() (4)
Quiero crear un futuro de tipo Future[(Class1,Class2,Class3)]
del código siguiente. Sin embargo, la única forma que he encontrado para hacerlo es usando zip (). Encuentro que la solución es fea y no óptima. ¿Alguien puede iluminarme?
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)]
También he intentado usar Future.sequence(List(f0, f1, f2))
pero esto no funcionará ya que el nuevo Future tendrá el tipo de Future[List[U]]
donde U
es el lubricante de Class1/2/3
mientras que yo quiero una 3-tupla preservando los tipos originales
Funcionantes Aplicativos
Lo que estás pidiendo es un funcionador aplicativo para un futuro. Ver el patrón Scalaz Applicative Builder . Debería ser bastante trivial enrollar el suyo en la parte posterior del zip
(f0 |@| f1 |@| f2)(g) //g is function (Class1, Class2, Class3) => Z
Esto es equivalente al aplicativo directo:
(f0 <***> (f1, f2))(g)
Scalaz se envía con un método de llaves de banana que forma una tupla del objetivo y los argumentos (es decir, lo que usted solicitó). Entonces tu solución será:
f0 <|**|> (f1, f2) //that. is. all.
Usted obtiene todo esto simplemente definiendo una instancia de clase de tipo para la siguiente clase de tipo:
trait Apply[Z[_]] {
def apply[A, B](f: Z[A => B], a: Z[A]): Z[B]
}
Entonces, para el futuro, esto se ve así:
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) }
}
}
(En realidad también necesitarías Pure
y Functor
. También podrías implementar Bind
mientras estás en ello - ver apéndice)
Lo bueno de este patrón es que comenzará a verlo en todas partes (por ejemplo, en Option
, en Validation
, en List
etc.). Por ejemplo, el producto cartesiano de 2 flujos es:
s1 <|*|> s2
Notas
Todo lo anterior suponiendo Scalaz 6, sin duda Scalaz 7 para 2.10 se enviará con estas clases de tipos por defecto. Pure
ha sido rebautizado como Pointed
en scalaz7.
Apéndice
Otras instancias de clase de tipo para el futuro:
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
}
Si está usando akka, mire el flujo de datos: http://doc.akka.io/docs/akka/2.0.2/scala/dataflow.html
necesita usar el complemento de Contingencias delimitadas (pero eso es fácil con sbt) y luego algo como:
val f:Future[(Class1,Class2,Class3)] = flow {
val f0 = process1
val f1 = process2
val f2 = process3
(f0(), f1(), f2())
}
debería compilar.
en build.sbt:
autoCompilerPlugins := true
addCompilerPlugin("org.scala-lang.plugins" % "continuations" % "2.9.1")
También puedes usar gatos:
import cats._
import cats.instances.future._
hay pocas formas útiles de hacer esto:
Primera opción más universal:
Applicative[Future].map3(f0, f1, f2){
(f0r, f1r, f2r) => //do something with results
}
y más simple :) que simplemente devolverá tuple Future [(f0.type, f1.type, f2.type)
Applicative[Future].tuple3(f0, f1, f2)
val result: Future[(Class1, Class2, Class3)] = {
val f1 = process1
val f2 = process2
val f3 = process3
for { v1 <- f1; v2 <- f2; v3 <- f3 } yield (v1, v2, v3)
}