Подтвердить что ты не робот

Как объединить фьючерсы разных типов в одно будущее без использования zip()

Я хочу создать Будущее типа Future[(Class1,Class2,Class3)] из ниже кода. Однако единственным способом, который я нашел для этого, является использование 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[List[U]], где U является lub Class1/2/3, тогда как я хочу, чтобы 3-кортеж сохранял исходные типы

4b9b3361

Ответ 1

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

Ответ 2

Аппликативные функторы

То, что вы просите, является прикладным функтором для будущего. См. Шаблон scalaz Applicative Builder. Это должно быть довольно тривиально, чтобы катиться на обратной стороне zip

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

Это эквивалентно прямому применению:

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

Scalaz поставляется с методом банановых фигурных скобок, который формирует кортеж от цели и аргументы (т.е. то, что вы просили). Таким образом, ваше решение будет:

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

Вы получите все это просто, указав экземпляр typeclass для следующего класса типов:

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

Итак, для будущего это выглядит так:

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

(На самом деле вам понадобится Pure и Functor. Можете также реализовать Bind, пока вы на нем - см. приложение)

Самое замечательное в этом шаблоне - это то, что вы начнете видеть его повсюду (например, в Option, в Validation, в List и т.д.). Например, декартово произведение двух потоков:

s1 <|*|> s2

Примечания

Все вышеперечисленное, предполагающее, что scalaz 6, несомненно, сказаз 7 для 2.10 будет поставляться с этими машинами по умолчанию. Pure был переименован в Pointed в scalaz7.


Приложение

Другие экземпляры класса типа для будущего:

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
}

Ответ 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")