await scala akka future applicative

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) }