无法使用 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,而正如我们刚刚看到的那样,它也可能是编译器强制执行一些不可能的证明,因为类型推断出错了,因为我们以错误的顺序解析它们。
我想从一元大小写 类 派生类型 类 的实例。但是当我尝试隐式推导它时,我总是会收到一条错误消息。如果我使用隐式方法明确地派生它 - 它有效。我不确定,但也许是因为我在函数中遗漏了一些隐式类型
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
配对的 - 然后证明
L
等于一些H :: HNil
在这个过程中计算出H
- 最终使用
H
获取Lazy[Foo[H]]
HList
L
编译成功Foo[A].hello()
。
当这两个最后的参数被交换到你的问题中时,编译器必须
- 假设一些
H
(在我们的测试用例中很可能不会是 Int) - 然后强行调整
L
来匹配 - 然后回到
Generic
现在被迫证明A
可以由一些H :: HNil
表示,其中H
很可能不是Int
并且没有这样做但是有误导性的错误信息
这是其中一个案例,表明基于无形的推导有时很棘手,并且在这里也表明无形作者在宏中假设宏扩展失败的最可能原因是 A
不是案例 class,而正如我们刚刚看到的那样,它也可能是编译器强制执行一些不可能的证明,因为类型推断出错了,因为我们以错误的顺序解析它们。