无形 HList 上的参数化折叠
Parameterized folding on a shapeless HList
我正在尝试实现一种方法,该方法对调用者提供的 HList 进行参数化折叠。 HList 可以有任意数量的相同类型的元素 (> 0)。
val list = "a" :: "b" :: "c" :: HNil
def process[L <: HList](mul: Int, l: L) = {
object combine extends Poly2 {
implicit def work = at[String, (Int, L)] {
case (a, (b, acc)) => (b, (a * b) :: acc)
}
}
l.foldRight((mul, HNil))(combine)._2
}
process(3, list) // expecting to get aaa :: bbb :: ccc :: HNil
我得到的是关于缺少隐式的错误:"could not find implicit value for parameter folder: shapeless.ops.hlist.RightFolder[L,(Int, shapeless.HNil.type),combine.type]"。从 this answer 可以明显看出,编译器希望看到它可以折叠 HList 的证据。
但是我无法将 RightFolder 作为隐式参数传递,因为 Poly2 类型在方法之外是未知的。即使可能,隐式参数也只会在调用堆栈中传播得更远。事实上,我什至不希望调用者知道该方法是否执行折叠、映射、缩减或其他任何操作。它需要提供的只是证明 HList 是正确类型的 HList 的证据。我认为问题出在 [L <: HList] 中,它不够具体,但我不确定如何使其正确。
以下代码按预期工作,但显然没有将折叠逻辑封装在方法中:
val list = "a" :: "b" :: "c" :: HNil
object combineS extends Poly2 {
implicit def work[L <: HList] = at[String, (Int, L)] {
case (a, (b, acc)) => (b, (a * b) :: acc)
}
}
list.foldRight((3, HNil))(combineS)._2
最简单的方法是提取combine
(添加类型参数L
)并将必要的隐式参数添加到process
。
object combine extends Poly2 {
implicit def work[L <: HList] = at[String, (Int, L)] {
case (a, (b, acc)) => (b, (a * b) :: acc)
}
}
def process[L <: HList](mul: Int, l: L)(implicit rightFolder: RightFolder.Aux[L, (Int, HNil.type), combine.type, _ <: (_,_)]) = {
l.foldRight((mul, HNil))(combine)._2
}
And even if it were possible, the implicit parameter would only propagate further up the call stack. In fact I don't want the caller to even know whether the method performs folding, mapping, reduction or anything else.
使用类型级编程,您可以将逻辑封装在类型 class 而不是方法中。所以可以引入一个类型class
trait Process[L <: HList] {
type Out <: HList
def apply(mul: Int, l: L): Out
}
object Process {
type Aux[L <: HList, Out0 <: HList] = Process[L] { type Out = Out0 }
object combine extends Poly2 {
implicit def work[L <: HList] = at[String, (Int, L)] {
case (a, (b, acc)) => (b, (a * b) :: acc)
}
}
implicit def mkProcess[L <: HList, Res, A, L1 <: HList](implicit
rightFolder: RightFolder.Aux[L, (Int, HNil.type), combine.type, Res],
ev: Res <:< (A, L1)
): Aux[L, L1] = new Process[L] {
override type Out = L1
override def apply(mul: Int, l: L): Out = l.foldRight((mul, HNil))(combine)._2
}
}
def process[L <: HList](mul: Int, l: L)(implicit p: Process[L]): p.Out = p(mul, l)
我正在尝试实现一种方法,该方法对调用者提供的 HList 进行参数化折叠。 HList 可以有任意数量的相同类型的元素 (> 0)。
val list = "a" :: "b" :: "c" :: HNil
def process[L <: HList](mul: Int, l: L) = {
object combine extends Poly2 {
implicit def work = at[String, (Int, L)] {
case (a, (b, acc)) => (b, (a * b) :: acc)
}
}
l.foldRight((mul, HNil))(combine)._2
}
process(3, list) // expecting to get aaa :: bbb :: ccc :: HNil
我得到的是关于缺少隐式的错误:"could not find implicit value for parameter folder: shapeless.ops.hlist.RightFolder[L,(Int, shapeless.HNil.type),combine.type]"。从 this answer 可以明显看出,编译器希望看到它可以折叠 HList 的证据。
但是我无法将 RightFolder 作为隐式参数传递,因为 Poly2 类型在方法之外是未知的。即使可能,隐式参数也只会在调用堆栈中传播得更远。事实上,我什至不希望调用者知道该方法是否执行折叠、映射、缩减或其他任何操作。它需要提供的只是证明 HList 是正确类型的 HList 的证据。我认为问题出在 [L <: HList] 中,它不够具体,但我不确定如何使其正确。
以下代码按预期工作,但显然没有将折叠逻辑封装在方法中:
val list = "a" :: "b" :: "c" :: HNil
object combineS extends Poly2 {
implicit def work[L <: HList] = at[String, (Int, L)] {
case (a, (b, acc)) => (b, (a * b) :: acc)
}
}
list.foldRight((3, HNil))(combineS)._2
最简单的方法是提取combine
(添加类型参数L
)并将必要的隐式参数添加到process
。
object combine extends Poly2 {
implicit def work[L <: HList] = at[String, (Int, L)] {
case (a, (b, acc)) => (b, (a * b) :: acc)
}
}
def process[L <: HList](mul: Int, l: L)(implicit rightFolder: RightFolder.Aux[L, (Int, HNil.type), combine.type, _ <: (_,_)]) = {
l.foldRight((mul, HNil))(combine)._2
}
And even if it were possible, the implicit parameter would only propagate further up the call stack. In fact I don't want the caller to even know whether the method performs folding, mapping, reduction or anything else.
使用类型级编程,您可以将逻辑封装在类型 class 而不是方法中。所以可以引入一个类型class
trait Process[L <: HList] {
type Out <: HList
def apply(mul: Int, l: L): Out
}
object Process {
type Aux[L <: HList, Out0 <: HList] = Process[L] { type Out = Out0 }
object combine extends Poly2 {
implicit def work[L <: HList] = at[String, (Int, L)] {
case (a, (b, acc)) => (b, (a * b) :: acc)
}
}
implicit def mkProcess[L <: HList, Res, A, L1 <: HList](implicit
rightFolder: RightFolder.Aux[L, (Int, HNil.type), combine.type, Res],
ev: Res <:< (A, L1)
): Aux[L, L1] = new Process[L] {
override type Out = L1
override def apply(mul: Int, l: L): Out = l.foldRight((mul, HNil))(combine)._2
}
}
def process[L <: HList](mul: Int, l: L)(implicit p: Process[L]): p.Out = p(mul, l)