【问题标题】:Using shapeless to convert tuple of Future to Future of tuple by way of HList使用 shapeless 通过 HList 将 Future 的 tuple 转换为 tuple 的 Future
【发布时间】:2014-05-04 07:06:10
【问题描述】:

有没有一种简单的方法可以将类型为 (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 上,由于其他依赖项而无法移动。

提前致谢!

【问题讨论】:

    标签: scala shapeless


    【解决方案1】:

    不知道这算不算“简单”,但Dan Lien 和我正在讨论如何对Options just the other daymy solution 的元组进行排序,因为这种情况可以直接适用于Future(请注意,我使用 scalaz-contrib 的 monad 实例作为 Future;如果您使用的是 Scalaz 7.1,则没有必要):

    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。必要的更改本质上是机械性的——如下所示应该可以:

    // 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 中有一个 sequence 实现,但由于各种原因(涉及类型推断),在这种情况下很难使用。

    【讨论】:

    • caseApplicative 的类型会是F[A :: B]吗?
    • 是的,但在 Case 包装器中。
    • 试试这个。也许我没有完全理解。 hlisted 不适用于类型 T。为了允许对 t 进行隐式转换,我不得不使用 def sequence[T <: product l hlist ...>
    • 你说得对——我没有尝试过 1.2.4 版本,因为我手边没有它。我刚刚尝试过,像this 这样的东西应该可以工作,但不知何故,这与RightFolderAux 的返回类型有关。有空我会去看看。
    猜你喜欢
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-25
    • 2015-07-30
    • 2016-05-04
    • 2017-01-27
    • 1970-01-01
    相关资源
    最近更新 更多