虽然其他答案所采取的方法(引入新的类型类)是可行的,但是我们可以使用更一般性的机制来解决这个问题——而且解决方式与在值层面解决类似问题的方式并没有太大区别。
我们将使用左折叠。编写组合函数有点棘手,因为我们有两种情况(我们已经看到了与当前元素相同类型的元素,或者我们还没有),我们必须使用隐式优先级技巧来避免模糊的隐式值:
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)
主要区别在于新的导入,但由于
updatedElem
上额外的类型参数,您还需要进行其他一些小的更改。
magic
函数,我现在完全没有时间,也许稍后我会发布一个解决方案,或者其他人会发布。 - DaunnC