隐式def中的Scala更高级别的类型失败,“无法找到隐含值”

时间:2016-04-29 18:46:05

标签: scala implicit higher-kinded-types

我正在使用隐式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]]

由于HConsHList的子类型,就像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)))
  }

我错过了什么?

谢谢:)

1 个答案:

答案 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]]

第一个只能在类型参数UHNil时使用,而另一个仅在UHCons时使用。但是HCons.++内部没有此信息。它只知道U <: HList,但不知道它是HList的哪个实现,因此失败。