无形:在hlist编译错误时foldLeft

时间:2016-07-26 08:44:15

标签: scala types shapeless

当折叠到List时,无法让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)

编译foldingHList2时的错误

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]

2 个答案:

答案 0 :(得分:3)

请注意,shapeless在runtimeList上定义了HList方法,该方法可以有效地计算List[Any]中的HList,以及转换为toList的{​​{1}}方法HList更精确地输入List。然而,看看如何使用fold执行此操作很有意思。

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

我们还需要将Nil作为初始值。 NilList[Nothing]类型的值,如果我们需要解析由List[T]建立的隐式索引,则会出现问题。类型变量T必须在Nothing处实例化以匹配Nil的类型,但不幸的是,Scala的类型推理器将类型Nothing视为值类型变量的含义"未解决的" ......结果是隐式解决方案会失败。要解决此问题,我们必须为foldListPoly提供Nil.type的明确案例。

把这些放在一起我们最终会,

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。

答案 1 :(得分:1)

问题是您在List[String]中为“aoeu”生成了wrapInList,该{a {1}}与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)