虽然在另一个答案中采用的方法(引入一个新的类型类)会起作用,但可以使用更通用的机制来解决这个问题——并且以一种与您在价值层面解决类似问题的方式没有什么不同的方式.
我们将使用左折叠。编写组合函数有点棘手,因为我们有两种情况(我们已经看到与当前元素具有相同类型的元素,或者我们没有看到),我们必须使用隐式优先级技巧来避免模棱两可隐含值:
import shapeless._
trait LowPriorityCombine extends Poly2 {
implicit def notAlreadySeen[L <: HList, A](implicit
p: Prepend[L, List[A] :: HNil]
) = at[L, List[A]](_ :+ _)
}
object combine extends LowPriorityCombine {
implicit def alreadySeen[L <: HList, A](implicit
s: Selector[L, List[A]],
r: Replacer[L, List[A], List[A]]
) = at[L, List[A]] {
case (acc, as) => acc.updatedElem[List[A]](acc.select[List[A]] ++ as)
}
}
但是我们基本上完成了:
def magic[L <: HList](l: L)(implicit f: LeftFolder[L, HNil.type, combine.type]) =
l.foldLeft(HNil)(combine)
我们可以证明它有效:
val xs = List(1, 2, 3) :: List('a, 'b) :: List("X", "Y") :: List(4, 5) :: HNil
val test = magic(xs)
进而:
scala> test == List(1, 2, 3, 4, 5) :: List('a, 'b) :: List("X", "Y") :: HNil
res0: Boolean = true
正如预期的那样。
上面的代码是为 1.2.4 编写的,但它应该可以在 2.0 上运行,只需进行一些非常小的修改。
更新:作为记录,这是 2.0 的工作版本:
import shapeless._, ops.hlist.{ LeftFolder, Prepend, Replacer, Selector }
trait LowPriorityCombine extends Poly2 {
implicit def notAlreadySeen[L <: HList, A, Out <: HList](implicit
p: Prepend.Aux[L, List[A] :: HNil, Out]
): Case.Aux[L, List[A], Out] = at[L, List[A]](_ :+ _)
}
object combine extends LowPriorityCombine {
implicit def alreadySeen[L <: HList, A, Out <: HList](implicit
s: Selector[L, List[A]],
r: Replacer.Aux[L, List[A], List[A], (List[A], Out)]
): Case.Aux[L, List[A], Out] = at[L, List[A]] {
case (acc, as) => acc.updatedElem[List[A], Out](acc.select[List[A]] ++ as)
}
}
def magic[L <: HList](l: L)(implicit f: LeftFolder[L, HNil, combine.type]) =
l.foldLeft(HNil: HNil)(combine)
主要区别在于新的导入,但由于额外的类型参数 on ,您还需要进行一些其他小的更改updatedElem
。