隐式 def 中的 Scala 高级类型失败并显示 "could not find implicit value"

Scala higher kinded types in implicit def fails with "could not find implicit value"

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

我错过了什么?

谢谢:)

你应该改变你的 ++ 方法定义:

 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)

编译器没有足够的信息来 select 方法定义中的正确隐式值,但是当您从外部传递 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[HNil, L, L]
Appender[HCons[T, L1], L2, HCons[T, R]]

只有类型参数UHNil时才可以使用第一个,只有UHCons时才可以使用另一个。但是 HCons.++ 中没有此信息。它只知道 U <: HList 但不知道 HList 是哪个实现,因此失败。