首页 > 解决方案 > 使用 shapeless.Generic 时,如何避免错误“超级构造函数不能传递自引用,除非参数是按名称声明的”?

问题描述

在现成的 scala 2.12 和 shapeless 2.3.2 下,以下简单程序无法编译:

import shapeless.Generic

object InferGeneric {

  class WithGeneric[T](implicit ev: Generic[T])

  case class Impl() {}

  object Impl extends WithGeneric[Impl]
}

编译器抛出以下错误:

/.../InferGeneric.scala:11: super constructor cannot be passed a self reference unless parameter is declared by-name
  object Impl extends WithGeneric[Impl]

有趣的是,当object Impl被重命名时,它可以毫无问题地编译。看起来通用推理中使用的宏在与伴随对象组合时会导致一些循环解析。如何避免这种情况?

非常感谢您的意见!

标签: scalaimplicitshapeless

解决方案


问题是宏生成的代码,但它并不是真正特定于宏的。您可以使用明确定义的实例重现该问题:

import shapeless._

class WithGeneric[T](implicit ev: Generic[T])

case class Impl()

object Impl extends WithGeneric[Impl]()(
  new Generic[Impl] {
    type Repr = HNil
    def to(p: Impl): Repr = HNil
    def from(p: Repr): Impl = Impl()
  }
)

或者,如果您想确保不涉及宏:

trait Generic[A] { def mk: A }

class WithGeneric[T](ev: Generic[T])

case class Impl()

object Impl extends WithGeneric[Impl](
  new Generic[Impl] { def mk: Impl = Impl() }
)

通常,在实例化伴随对象时,您将无法传递Impl.apply在构造函数调用中调用的代码。Impl

如果不了解更多有关您要使用的内容,很难提出解决方法WithGeneric。在像这样的简单情况下,您可以Generic[Impl]显式定义并且只使用new Impl构造函数(不是Impl.apply)。如果您想要的是能够为伴随对象的便利方法提供对案例类进行抽象的定义,您可以执行以下操作:

import shapeless._

abstract class WithGeneric[T] {
  def ev: Generic[T]
}

case class Impl()

object Impl extends WithGeneric[Impl] {
  def ev: Generic[Impl] = Generic[Impl]
}

这是一个小样板,但在不了解您的用例的情况下,我猜这可能是您最好的选择。


推荐阅读