【问题标题】:Fold a Shapeless HList into Future of that same HList, flattening Future elements found, if any将 Shapeless HList 折叠到同一个 HList 的 Future 中,如果有的话,将找到的 Future 元素展平
【发布时间】:2020-04-05 01:37:14
【问题描述】:

我正在尝试将任意 HList 转换为 Future[HList],其中所有 Future 元素都被展平。

例如:

  • Int :: Future[String] :: HNil变成Future[Int :: String :: HNil]
  • Future[Boolean] :: String :: Int :: HNil变成Future[Boolean :: String :: Int :: HNil]

到目前为止,我已经能够使用 foldRight 将一个 HList of Futures 转换为他们的结果的 Future。

import scala.concurrent.duration.Duration
import scala.concurrent.{Await, ExecutionContext, Future}
import shapeless._

object joinFutures extends Poly2 {
  implicit def future[L, R <: HList](implicit ec: ExecutionContext): Case[Future[L], Future[R]] {
    type Result = Future[L :: R]
  } = at[Future[L], Future[R]] { (futureL, futureR) =>
    futureR.flatMap(r => futureL.map(_ :: r))
  }
}

object Example extends App {
  import ExecutionContext.Implicits.global
  import Future.{successful => F}

  type In = Future[Int] :: Future[String] :: Future[Int] :: Future[String] :: HNil
  type Out = Future[Int :: String :: Int :: String :: HNil]
  val in: In = F(1) :: F("asd") :: F(2) :: F("qwe") :: HNil
  val out: Out = in.foldRight(Future.successful(HNil))(joinFutures)
  println(Await.result(out, Duration.Inf)) // 1 :: asd :: 2 :: qwe :: HNil
}

然后我尝试在我的 Poly2 中引入默认情况,对于所有其他情况。

object joinFutures extends Poly2 {
  implicit def future[L, R <: HList](implicit ec: ExecutionContext): Case[Future[L], Future[R]] {
    type Result = Future[L :: R]
  } = at[Future[L], Future[R]] { (lFuture, futureR) =>
    futureR.flatMap(r => lFuture.map(_ :: r))
  }
  implicit def default[L, R <: HList](implicit ec: ExecutionContext): Case[L, Future[R]] {
    type Result = Future[L :: R]
  } = at[L, Future[R]] { (l, futureR) =>
    futureR.map(r => l :: r)
  }
}

但是在调用 foldRight 时编译失败并出现错误,指出它找不到文件夹 RightFolder[In,Future[HNil.type],joinFutures.type] 的隐式。

我想问题是 joinFutures 现在有冲突的情况,因为 Future 类型的 HList 元素匹配 future default 情况。

我的目标是匹配任何元素 except 期货,但显然编译器无法从上面的代码中推断出来。

是否有可能使用foldRight 来实现这种转换?怎么样?

【问题讨论】:

    标签: scala shapeless


    【解决方案1】:

    与此同时,我找到了解决方案,所以我会回答我自己的问题。

    是的,可以使用foldRight 来实现。

    我们的想法是通过“将有问题的隐式定义移动到低优先级的超级特征”来解决这种隐含的歧义,如 Github issue 中所述。

    这是一个工作示例。

    import scala.concurrent.duration.Duration
    import scala.concurrent.{Await, ExecutionContext, Future}
    import shapeless._
    
    trait joinFuturesDefault extends Poly2 {
      implicit def default[L, R <: HList](implicit ec: ExecutionContext): Case[L, Future[R]] {
        type Result = Future[L :: R]
      } = at[L, Future[R]] { (l, futureR) =>
        futureR.map(r => l :: r)
      }
    }
    
    object joinFutures extends joinFuturesDefault {
      implicit def future[L, R <: HList](implicit ec: ExecutionContext): Case[Future[L], Future[R]] {
        type Result = Future[L :: R]
      } = at[Future[L], Future[R]] { (futureL, futureR) =>
        futureR.flatMap(r => futureL.map(_ :: r))
      }
    }
    
    object Example extends App {
      import ExecutionContext.Implicits.global
      import Future.{successful => F}
    
      type In = Int :: Future[String] :: Future[Int] :: String :: HNil
      type Out = Future[Int :: String :: Int :: String :: HNil]
      val in: In = 1 :: F("asd") :: F(2) :: "qwe" :: HNil
      val out: Out = in.foldRight(Future.successful(HNil))(joinFutures)
      println(Await.result(out, Duration.Inf)) // 1 :: asd :: 2 :: qwe :: HNil
    }
    

    通过这种方式,我们实际上指示编译器首先匹配 joinFutures 中的用例,并仅在 joinFutures 中没有匹配时才继续使用 joinFuturesDefault 中的用例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-29
      • 1970-01-01
      • 2016-04-23
      • 2023-03-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多