我编写了一个独立的示例来重现该问题。

为了更好地理解这个问题,我编写了一个简单的类型类Encoder,它对给定类型T进行编码:

trait Encoder[T] {
  def encode(value: T): String
}

其伴随对象具有良好的语法,而速记函数可用于创建类型类实例:
object Encoder {
  def instance[T](f: T => String): Encoder[T] = new Encoder[T] {
    def encode(value: T): String = f(value)
  }

  def apply[T](implicit encoder: Encoder[T]): Encoder[T] = encoder

  object syntax {
    implicit class EncoderOps[T](value: T) {
      def encode(implicit encoder: Encoder[T]): String = encoder.encode(value)
    }
  }
}

有一个称为Fragment的ADT用于表示页面的片段,我已经为所有片段类型实现了Encoder。
sealed trait Fragment
final case class TextFragment(value: String) extends Fragment
object TextFragment {
  implicit val encoder: Encoder[TextFragment] = Encoder.instance(_.value)
}

final case class NumberFragment(value: Int) extends Fragment
object NumberFragment {
  implicit val encoder: Encoder[NumberFragment] =
    Encoder.instance(_.value.toString)
}

为了使像这样的通话成为可能:
val fragments: List[Fragment] =
  List(TextFragment("text fragment"), NumberFragment(123))

println {
  fragments.map(_.encode).mkString("\n")
}

我已经在Shapeless的帮助下实现了Coproduct和HList的通用编码器:
implicit def hlistEncoder[H, T <: HList](
    implicit
    hEncoder: Lazy[Encoder[H]],
    tEncoder: Encoder[T]
): Encoder[H :: T] = instance {
  case h :: t => hEncoder.value.encode(h) ++ tEncoder.encode(t)
}

implicit def genericEncoder[A, Repr](implicit gen: Generic.Aux[A, Repr],
                                     encoder: Encoder[Repr]): Encoder[A] =
  instance(fragment => encoder.encode(gen.to(fragment)))

implicit val cnilEncoder: Encoder[CNil] = instance(
  cnil => throw new Exception("not allowed"))

implicit def coproductEncoder[H, T <: Coproduct](
    implicit hEncoder: Lazy[Encoder[H]],
    tEncoder: Encoder[T]
): Encoder[H :+: T] = instance {
  case Inl(h) => hEncoder.value.encode(h)
  case Inr(t) => tEncoder.encode(t)
}

它按预期工作,但是...

...如果我添加一个像下面这样的通用片段,它将不起作用:
final case class GenericFragment[T](value: T) extends Fragment
object GenericFragment {
  implicit def encoder[T](
      implicit encoder: Encoder[T]): Encoder[GenericFragment[T]] =
    Encoder.instance(fragment => encoder.encode(fragment.value))
}

以及一些用于Int和String值的编码器:
implicit val stringEncoder: Encoder[String] = instance(identity)
implicit val intEncoder: Encoder[Int] = instance(_.toString)

然后它不再编译...
val fragments: List[Fragment] =
  List(TextFragment("text fragment"), NumberFragment(123))

println {
  fragments.map(_.encode).mkString("\n")
}

这是在scalacOptions中带有-Xlog-implicits的输出:
[info] /Users/../example/src/main/scala/playground/PlaygroundApp.scala:92:21: shapeless.this.Generic.materialize is not a valid implicit value for shapeless.Generic.Aux[playground.Fragment,Repr] because:
[info] hasMatchingSymbol reported error: exception during macro expansion:
[info] java.lang.IndexOutOfBoundsException: -1
[info]  at scala.collection.LinearSeqOptimized.apply(LinearSeqOptimized.scala:63)
[info]  at scala.collection.LinearSeqOptimized.apply$(LinearSeqOptimized.scala:61)
[info]  at scala.collection.immutable.List.apply(List.scala:86)
[info]  at shapeless.CaseClassMacros.$anonfun$ctorsOfAux$4(generic.scala:421)
[info]  at scala.collection.immutable.List.map(List.scala:283)
[info]  at shapeless.CaseClassMacros.substituteArgs$1(generic.scala:419)
[info]  at shapeless.CaseClassMacros.$anonfun$ctorsOfAux$3(generic.scala:446)
[info]  at scala.collection.immutable.List.flatMap(List.scala:335)
[info]  at shapeless.CaseClassMacros.ctorsOfAux(generic.scala:413)
[info]  at shapeless.CaseClassMacros.ctorsOfAux$(generic.scala:382)
[info]  at shapeless.GenericMacros.ctorsOfAux(generic.scala:989)
[info]  at shapeless.CaseClassMacros.distinctCtorsOfAux(generic.scala:379)
[info]  at shapeless.CaseClassMacros.distinctCtorsOfAux$(generic.scala:374)
[info]  at shapeless.GenericMacros.distinctCtorsOfAux(generic.scala:989)
[info]  at shapeless.CaseClassMacros.ctorsOf(generic.scala:371)
[info]  at shapeless.CaseClassMacros.ctorsOf$(generic.scala:371)
[info]  at shapeless.GenericMacros.ctorsOf(generic.scala:989)
[info]  at shapeless.GenericMacros.mkCoproductGeneric(generic.scala:1036)
[info]  at shapeless.GenericMacros.materialize(generic.scala:1003)
[info]     fragments.map(_.encode).mkString("\n")
[info]                     ^
[info] /Users/../example/src/main/scala/playground/PlaygroundApp.scala:92:21: playground.this.Encoder.genericEncoder is not a valid implicit value for playground.Encoder[playground.Fragment] because:
[info] hasMatchingSymbol reported error: could not find implicit value for parameter gen: shapeless.Generic.Aux[playground.Fragment,Repr]
[info]     fragments.map(_.encode).mkString("\n")
[info]                     ^
[info] /Users/../example/src/main/scala/playground/PlaygroundApp.scala:92:21: shapeless.this.Generic.materialize is not a valid implicit value for shapeless.Generic.Aux[playground.Fragment,Repr] because:
[info] hasMatchingSymbol reported error: exception during macro expansion:
[info] java.lang.IndexOutOfBoundsException: -1
[info]  at scala.collection.LinearSeqOptimized.apply(LinearSeqOptimized.scala:63)
[info]  at scala.collection.LinearSeqOptimized.apply$(LinearSeqOptimized.scala:61)
[info]  at scala.collection.immutable.List.apply(List.scala:86)
[info]  at shapeless.CaseClassMacros.$anonfun$ctorsOfAux$4(generic.scala:421)
[info]  at scala.collection.immutable.List.map(List.scala:283)
[info]  at shapeless.CaseClassMacros.substituteArgs$1(generic.scala:419)
[info]  at shapeless.CaseClassMacros.$anonfun$ctorsOfAux$3(generic.scala:446)
[info]  at scala.collection.immutable.List.flatMap(List.scala:335)
[info]  at shapeless.CaseClassMacros.ctorsOfAux(generic.scala:413)
[info]  at shapeless.CaseClassMacros.ctorsOfAux$(generic.scala:382)
[info]  at shapeless.GenericMacros.ctorsOfAux(generic.scala:989)
[info]  at shapeless.CaseClassMacros.distinctCtorsOfAux(generic.scala:379)
[info]  at shapeless.CaseClassMacros.distinctCtorsOfAux$(generic.scala:374)
[info]  at shapeless.GenericMacros.distinctCtorsOfAux(generic.scala:989)
[info]  at shapeless.CaseClassMacros.ctorsOf(generic.scala:371)
[info]  at shapeless.CaseClassMacros.ctorsOf$(generic.scala:371)
[info]  at shapeless.GenericMacros.ctorsOf(generic.scala:989)
[info]  at shapeless.GenericMacros.mkCoproductGeneric(generic.scala:1036)
[info]  at shapeless.GenericMacros.materialize(generic.scala:1003)
[info]     fragments.map(_.encode).mkString("\n")
[info]                     ^
[info] /Users/../example/src/main/scala/playground/PlaygroundApp.scala:92:21: playground.this.Encoder.genericEncoder is not a valid implicit value for playground.Encoder[playground.Fragment] because:
[info] hasMatchingSymbol reported error: could not find implicit value for parameter gen: shapeless.Generic.Aux[playground.Fragment,Repr]
[info]     fragments.map(_.encode).mkString("\n")
[info]                     ^
[error] /Users/../example/src/main/scala/playground/PlaygroundApp.scala:92:21: could not find implicit value for parameter encoder: playground.Encoder[playground.Fragment]
[error]     fragments.map(_.encode).mkString("\n")

使用的版本:
  • Scala:2.12.5
  • shapeless:2.3.3

  • 为什么它不适用于通用片段类型?有人可以解释一下为什么吗?希望有一个解决此问题的好方法。

    谢谢您的帮助。

    最佳答案

    看起来问题不在于引入泛型。如果我们看这段代码

    val fragments: List[Fragment] =
      List(TextFragment("text fragment"), NumberFragment(123))
    

    我们认识到List将其类型的第一个公共边界作为类型参数Fragment。使用此分配,您丢失了有关片段的类型信息。 ListCoproduct,但范围内没有任何Encoder[Fragment],因此implicit def coproductEncoder不适用。

    如果确实需要保留类型参数,则需要使用HListEncoderOps[TextFragment] :: EncoderOps[NumberFragment]],或者像这样简单地摆脱类型参数:
    trait Encodable{
         def encode:String
    }
    
    object Encodable{
          implicit def asEncodable[T](t:T)(implicit encoder:Encoder[T]) = new Encodable {
                  def encode = encoder.encode(t)
           }
    
    }
    
    val encodableFragments:List[Encodable] = List(TextFragment("text fragment"), NumberFragment(123))
    

    关于scala - 如果Coproduct成员在我的ADT中是通用的,为什么它不能解析隐式?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49851942/

    10-11 03:40