我正在使用隐式def来构建递归HList
类型,以匹配几种更高级别的HList
类型。我受到this post的启发。
此代码完美运行:
sealed trait HList {
type Plus[L <: HList] <: HList
}
class HNil extends HList {
type Plus[L <: HList] = L
def ::[T](v: T) = HCons(v, this)
}
case class Appender[L1 <: HList, L2 <: HList, R <: HList](fn: (L1, L2) => R) {
def apply(l1: L1, l2: L2) = fn(l1, l2)
}
object HNil extends HNil
object HList {
def ++[L1 <: HList, L2 <: HList](l1: L1, l2: L2)(implicit f: Appender[L1, L2, L1#Plus[L2]]): L1#Plus[L2] = f(l1, l2)
implicit def nilAppender[L <: HList]: Appender[HNil, L, L] = Appender((v: HNil, l: L) => l)
implicit def consAppender[T, L1 <: HList, L2 <: HList, R <: HList](implicit f: Appender[L1, L2, R]): Appender[HCons[T, L1], L2, HCons[T, R]] = {
Appender[HCons[T, L1], L2, HCons[T, R]]((l1: HCons[T, L1], l2: L2) => HCons(l1.head, f(l1.tail, l2)))
}
}
case class HCons[T, U <: HList](head: T, tail: U) extends HList {
type Plus[L <: HList] = HCons[T, U#Plus[L]]
def ::[V](v: V) = HCons(v, this)
}
import HList._
val hlist1 = 2.0 :: "hi" :: HNil
val hlist2 = 1 :: HNil
val sum = ++(hlist1, hlist2)
println("last element : " : + sum.tail.tail.head) // prints last element : 1"
现在,我不知道为什么,但是如果我尝试在++
上添加HCons
方法,它只是调用现有的HList.++
方法,那么这不起作用:
case class HCons[T, U <: HList](head: T, tail: U) extends HList {
type Plus[L <: HList] = HCons[T, U#Plus[L]]
def ::[V](v: V) = HCons(v, this)
def ++[L2 <: HList](l2: L2) = HList.++(this,l2)
}
我收到此编译错误:
could not find implicit value for parameter f: Appender[HCons[T,U],L2,HCons[T,U]#Plus[L2]]
由于HCons
是HList
的子类型,就像HList。++定义的L1类型一样,我认为它没问题。
我已经尝试了这个但是效果不佳:
implicit def consAppender[T, L1 <: HList, L2 <: HList, L3, R <: HList](implicit f: Appender[L1, L2, R], ev: L3 <:< HCons[T, L1]): Appender[HCons[T, L1], L2, HCons[T, R]] = {
Appender[HCons[T, L1], L2, HCons[T, R]]((l1: L3, l2: L2) => HCons(l1.head, f(l1.tail, l2)))
}
我错过了什么?
谢谢:)
答案 0 :(得分:10)
您应该更改++
方法定义:
def ++[L2 <: HList](l2: L2) = HList.++(this,l2)
到此:
def ++[L2 <: HList](l2: L2)(implicit f: Appender[HCons[T,U], L2, Plus[L2]]) = HList.++(this,l2)
编译器没有足够的信息在方法定义中选择正确的隐式值,但是当你从外部传递appender时,这个例子应该通过:
val hlist1 = 2.0 :: "hi" :: HNil
val hlist2 = 1 :: HNil
println(hlist1++hlist2)
更新1:在++
上的HCons
方法中,我们调用需要隐式参数的HList.++
方法。此参数必须是Appender[HCons[T, U], L2, HCons[T, U#Plus[L2]]]
类型。编译器可以从HList.consAppender
填充此隐式参数,但这又需要另一个类型为Appender[U, L2, U#Plus[L2]]
的隐式参数。 这是编译器无法发现的参数。知道了这一点,上面的代码可以简化为:
def ++[L2 <: HList](l2: L2)(implicit f: Appender[U, L2, U#Plus[L2]]): Plus[L2] = HList.++(this, l2)
更新2:编译器必须在调用站点填写隐式参数,在我们的案例中使用HCons.++
方法(可以使用scalac -Xprint:typer
进行验证)。它可以选择提供两种appender类型的implicits:
Appender[HNil, L, L]
Appender[HCons[T, L1], L2, HCons[T, R]]
第一个只能在类型参数U
为HNil
时使用,而另一个仅在U
为HCons
时使用。但是HCons.++
内部没有此信息。它只知道U <: HList
,但不知道它是HList
的哪个实现,因此失败。