使用shapeless通过HList将Future的tuple转换为tuple的Future [英] Using shapeless to convert tuple of Future to Future of tuple by way of HList

查看:72
本文介绍了使用shapeless通过HList将Future的tuple转换为tuple的Future的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否有一种简单的方法可以将类型为 (Future[A], Future[B], Future[C], ..., Future[N]) 的元组转换为 Future[(A, B, C, ..., N)]?这假设元组中有未定义数量的元素.

Is there an easy way to convert a tuple of type (Future[A], Future[B], Future[C], ..., Future[N]) to Future[(A, B, C, ..., N)]? This assumes an undefined number of elements in the tuple.

我已经尝试将元组转换为 HList 并尝试了类似的 foldLeft 技巧和理解,就像在 Future.sequence 中所做的那样,但在处理传入折叠的类型方面没有运气.对递归函数进行了同样的尝试.但是由于缺少 HList.head、HList.tail,这段代码仍然无法编译.代码如下所示:

I've tried converting the tuple to HList and tried a similar trick of foldLeft and for comprehension as done in Future.sequence but no luck dealing with the types passed into the fold. Tried the same with recursive functions. But this code still does not compile due to missing HList.head, HList.tail. The code looks like follows:

def sequence[L <: HList](in: L)(implicit ihc: IsHCons[L]) = {

  val list = for (i <- in.head; j <- in.tail.head) yield HList(i, j)

  @tailrec
  def sequence2(fList: Future[HList], listF: HList): Future[HList] = {
    if (listF == HNil) fList
    else {
      val nextFList = for (l <- fList; e <- listF.head.asInstanceOf[Future[_]]) yield l :+ e
      sequence2(nextFList, listF.tail)
    }
  }

  sequence2(list, in.tail.tail)
}

此代码应返回 Future[HList],然后我们可以使用 tupled 函数将其映射回元组.理想情况下,我们需要检查元组中的元素少于 3 个这一事实.但是让我们假设输入是一个大小为 3 或更大的 HList.

This code should return Future[HList] which we can then map back to tuple with the tupled function. Ideally we need to check for the fact we have less than 3 elements in the tuple. But lets assume the input is an HList of size 3 or larger for this exercise.

我使用的是 Shapeless 1.2.4,但由于其他依赖项而无法移动.

I'm on Shapeless 1.2.4 and can't move yet due to other dependencies.

提前致谢!

推荐答案

不知道这算不算简单",但是Dan Lien 和我正在讨论如何对 Options 就在前几天的元组进行排序,然后我针对这种情况的解决方案可以直接适用于Future(请注意,我为 Future 使用 scalaz-contrib 的 monad 实例;如果你使用的是 Scalaz 7.1没有必要):

Don't know whether this counts as "easy", but Dan Lien and I were discussing how to sequence tuples of Options just the other day, and my solution for that case can be straightforwardly adapted to work for Future (note that I'm using scalaz-contrib's monad instance for Future; if you're on Scalaz 7.1 this isn't necessary):

import scala.concurrent.{ ExecutionContext, Future }
import scalaz._, Scalaz._, contrib.std.scalaFuture._
import shapeless._, ops.hlist.{ RightFolder, Tupler }

// Might as well stay generic in `F` for this part.
object applicativeFolder extends Poly2 {
  implicit def caseApplicative[A, B <: HList, F[_]](implicit
    app: Applicative[F]
  ) = at[F[A], F[B]] {
    (a, b) => app.ap(a)(app.map(b)(bb => (_: A) :: bb))
  }
}

// It should be possible to make this part generic in `F` as well,
// but type inference makes it tricky, so we specialize to `Future`.
def sequence[T, EL <: HList, L <: HList, OL <: HList, OT](t: T)(implicit
  executor: ExecutionContext,
  gen: Generic.Aux[T, EL],
  eq: EL =:= L,
  folder: RightFolder.Aux[L, Future[HNil], applicativeFolder.type, Future[OL]],
  tupler: Tupler.Aux[OL, OT]
): Future[OT] =
  eq(gen.to(t)).foldRight(Future.successful(HNil: HNil))(applicativeFolder).map(
    tupler(_)
  )

哦,刚刚注意到您使用的是 1.2.4.必要的更改本质上是机械性的 - 应该可以使用以下内容:

Oh, just noticed that you're on 1.2.4. The necessary changes are essentially mechanical—something like the following should work:

// It should be possible to make this part generic in `F` as well,
// but type inference makes it tricky, so we specialize to `Future`.
def sequence[T, L <: HList, OL <: HList, OT](t: T)(implicit
  executor: ExecutionContext,
  hlister: HListerAux[T, L],
  folder: RightFolderAux[L, Future[HNil], applicativeFolder.type, Future[OL]],
  tupler: TuplerAux[OL, OT]
): Future[OT] =
  t.hlisted.foldRight(Future.successful(HNil: HNil))(applicativeFolder).map(
    tupler(_)
  )

它是这样工作的:

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

scala> val result = sequence((Future(1), Future('a)))
result: scala.concurrent.Future[(Int, Symbol)] = ...

scala> result.foreach(println)
(1,'a)

请注意,shapeless-contrib,但由于各种原因(涉及类型推断)在这种情况下很难使用.

Note that there is a sequence implementation in shapeless-contrib, but for various reasons (involving type inference) it's difficult to use in this situation.

这篇关于使用shapeless通过HList将Future的tuple转换为tuple的Future的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆