使用无形通过HList将Future的元组转换为元组的Future

时间:2014-05-04 07:06:11

标签: scala shapeless

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

我已经尝试将元组转换为HList并尝试了类似的foldLeft技巧,并且在Future.sequence中进行了理解,但没有运气处理传递给折叠的类型。尝试使用递归函数。但由于缺少HList.head,HList.tail,此代码仍然无法编译。代码如下所示:

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。

我正在使用Shapeless 1.2.4并且由于其他依赖性而无法移动。

提前致谢!

1 个答案:

答案 0 :(得分:10)

不知道这是否算作&#34; easy&#34;,但Dan Lien我正在讨论如何对Option s just the other day的元组进行排序,以及my solution对于该案例可以直接适用于Future(请注意我Future使用scalaz-contrib的monad实例;如果你&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(_) ) #39;依靠Scalaz 7.1,这不是必要的):

// 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(_)
  )

哦,只是注意到你在1.2.4。必要的变化基本上是机械的 - 类似下面的东西应该起作用:

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)

它的工作原理如下:

sequence

请注意shapeless-contrib中有{{1}}个实现,但由于各种原因(涉及类型推断),在这种情况下难以使用。