【问题标题】:Shapeless: foldLeft on hlist compilation errorShapeless: foldLeft on hlist 编译错误
【发布时间】:2016-11-29 19:35:17
【问题描述】:

折叠到列表时无法让 foldLeft 在 HList 上工作,当我将 Hlist 折叠到字符串时一切正常。

object wrapInList extends Poly1 {
  implicit def intCase = at[Int]{v => List(v)}
  implicit def stringCase = at[String]{v => List(v)}
}

object mergeToString extends Poly1 {
  implicit def intCase = at[Int](_.toString())
  implicit def stringCase = at[String](identity)
}

object foldListPoly extends Poly2 {
  implicit  def foldToList[T](implicit st: wrapInList.Case.Aux[T, List[T]]) =
    at[List[T], T]{ (acc, t) => acc ::: wrapInList(t) }
  implicit def foldToString[T](implicit st: mergeToString.Case.Aux[T, String]) =
    at[String, T]{ (acc, t) => acc + mergeToString(t)}
}

val hList = "aoeu" :: 42 :: HNil
val foldedHlist = hList.foldLeft("")(foldListPoly)
val foldedHList2 = hList.foldLeft(Nil)(foldListPoly)

编译 foldedHList2 时出错

could not find implicit value for parameter folder:
shapeless.ops.hlist.LeftFolder[shapeless.::[String,shapeless.::[Int,shapeless.HNil]],scala.collection.immutable.Nil.type,com.test.Test.foldListPoly.type]

【问题讨论】:

  • 能补充一下错误吗?
  • 我用编译错误更新了问题。

标签: scala types shapeless


【解决方案1】:

问题是您在wrapInList 中为“aoeu”生成了一个List[String],它与hList 的下一个元素不兼容(42,这是一个Int)。如果您可以使用List[Any](而不是最小上限),您可以这样做:

object wrapInList extends Poly1 {
  implicit def intCase = at[Int]{v => List[Any](v)}
  implicit def stringCase = at[String]{v => List[Any](v)}
}

object foldListPoly extends Poly2 {
  implicit  def foldToList[T](implicit st: wrapInList.Case.Aux[T, List[Any]]) =
    at[List[Any], T]{ (acc, t) => acc ::: wrapInList(t) }
}

此外,您必须将Nil 显式键入为List[Any]:

val foldedHList2 = hList.foldLeft(Nil:List[Any])(foldListPoly)

【讨论】:

  • 我想到了不兼容的类型,但不幸的是,这段代码无法编译,并且在尝试折叠 hList 时会出现同样的错误。
  • 抱歉,我忘了包含包含List[Any] 类型的方法调用。
【解决方案2】:

请注意,shapeless 在HList 上定义了一个runtimeList 方法,该方法有效地从HList 计算List[Any],还有一个toList 方法,将HList 转换为更精确类型的@987654327 @。不过,看看如何使用fold 执行此操作很有趣。

这里有一些皱纹。首先要考虑的是你在这里积累的List 的类型应该是什么。您很可能希望结果列表的元素类型是您要折叠的 HList 的元素类型的最小上限 (LUB)。为了得到这种类型的结果,我们需要在进行时计算 LUB。

我们还需要容纳Nil 作为初始值。 Nil 是 List[Nothing] 类型的值,这在我们需要解析由 List[T] 索引的隐式索引的情况下是有问题的。类型变量T 必须在Nothing 实例化以匹配Nil 的类型,但不幸的是,Scala 的类型推断器将类型Nothing 视为类型变量的值,表示“未解决”......这样做的结果是隐式解析会虚假地失败。为了解决这个问题,我们必须为Nil.type 提供一个明确的foldListPoly 案例。

把这些放在一起,我们最终得到了,

object wrapInList extends Poly1 {
  implicit def default[T] = at[T]{v => List(v)}
}

object foldListPoly extends Poly2 {
  implicit  def foldToListNil[U](implicit st: wrapInList.Case.Aux[U, List[U]]) =
    at[Nil.type, U]{ (acc, u) => wrapInList(u) }

  implicit  def foldToList[T, U, L, LL](
    implicit st: wrapInList.Case.Aux[U, List[U]],
    lub: Lub[T, U, L],
    llub: Lub[List[T], List[U], LL],
    ev: LL =:= List[L]
  ) = at[List[T], U]{ (acc, u) => llub.left(acc) ::: llub.right(wrapInList(u)) }
}

scala> ("aoeu" :: 42 :: HNil).foldLeft(Nil)(foldListPoly)
res0: List[Any] = List(aoeu, 42)

scala> (13 :: 23 :: HNil).foldLeft(Nil)(foldListPoly)
res1: List[Int] = List(13, 23)

scala> (23 :: true :: HNil).foldLeft(Nil)(foldListPoly)
res2: List[AnyVal] = List(23, true)

请注意,在每种情况下,结果列表的元素类型都是 HList 的元素类型的 LUB。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    相关资源
    最近更新 更多