无法使用 shapeless 派生类型 class 的隐式实例

Cannot derive implicit instance of type class using shapeless

我想从一元大小写 类 派生类型 类 的实例。但是当我尝试隐式推导它时,我总是会收到一条错误消息。如果我使用隐式方法明确地派生它 - 它有效。我不确定,但也许是因为我在函数中遗漏了一些隐式类型

import shapeless._

import scala.reflect.ClassTag
import scala.reflect.runtime.universe.TypeTag

sealed trait Foo[A] {
  def hello(): Unit
}

object Foo {
  def apply[A](implicit foo: Foo[A]): foo.type = foo

  def instance[A](implicit tag: ClassTag[A]): Foo[A] = new Foo[A] {
    override def hello(): Unit = println(s"Hello from ${tag.runtimeClass.getName}")
  }
}

trait Instances extends LowestPriority {
  implicit val intHelloInstance: Foo[Int] = Foo.instance[Int]
}

trait LowestPriority {
  implicit def derive[A: TypeTag, L <: HList, H](
    implicit gen: Generic.Aux[A, L],
    H: Lazy[Foo[H]],
    isUnary: (H :: HNil) =:= L
  ): Foo[A] =
    new Foo[A] {
      override def hello(): Unit = {
        print(s"Derived: ")
        H.value.hello()
      }
    }
}

object T extends Instances {

  case class A(a: Int)

  def main(args: Array[String]): Unit = {
    intHelloInstance.hello()
//    val a: Foo[A] = derive[A, Int :: HNil, Int] // this works
//    a.hello()
    Foo[A].hello() // error
  }
}

来自日志:

Information:(45, 8) shapeless.this.Generic.materialize is not a valid implicit value for shapeless.Generic.Aux[H,L] because: hasMatchingSymbol reported error: H is not a case class, case class-like, a sealed trait or Unit Foo[A].hello()

我该如何解决?

这是其中一种情况,其中行为取决于...要解析的隐含顺序。

如果将签名修改为:

  implicit def derive[A, L <: HList, H](
    implicit gen: Generic.Aux[A, L],
    isUnary: (H :: HNil) =:= L, // swapped
    H: Lazy[Foo[H]]             // with this
  ): Foo[A] = ...

编译器将:

  • 尝试找到一些可以与 A
  • 配对的 HList L
  • 然后证明 L 等于一些 H :: HNil 在这个过程中计算出 H
  • 最终使用 H 获取 Lazy[Foo[H]]

编译成功Foo[A].hello()

当这两个最后的参数被交换到你的问题中时,编译器必须

  • 假设一些 H(在我们的测试用例中很可能不会是 Int)
  • 然后强行调整L来匹配
  • 然后回到 Generic 现在被迫证明 A 可以由一些 H :: HNil 表示,其中 H 很可能不是 Int并且没有这样做但是有误导性的错误信息

这是其中一个案例,表明基于无形的推导有时很棘手,并且在这里也表明无形作者在宏中假设宏扩展失败的最可能原因是 A 不是案例 class,而正如我们刚刚看到的那样,它也可能是编译器强制执行一些不可能的证明,因为类型推断出错了,因为我们以错误的顺序解析它们。