我编写了一个独立的示例来重现该问题。
为了更好地理解这个问题,我编写了一个简单的类型类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")
使用的版本:
为什么它不适用于通用片段类型?有人可以解释一下为什么吗?希望有一个解决此问题的好方法。
谢谢您的帮助。
最佳答案
看起来问题不在于引入泛型。如果我们看这段代码
val fragments: List[Fragment] =
List(TextFragment("text fragment"), NumberFragment(123))
我们认识到
List
将其类型的第一个公共边界作为类型参数Fragment
。使用此分配,您丢失了有关片段的类型信息。 List
是Coproduct
,但范围内没有任何Encoder[Fragment]
,因此implicit def coproductEncoder
不适用。如果确实需要保留类型参数,则需要使用
HList
的EncoderOps[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/