gpt4 book ai didi

scala - 如果 Coproduct 成员在我的 ADT 中是通用的,为什么它不能解析隐式?

转载 作者:行者123 更新时间:2023-12-01 06:55:51 25 4
gpt4 key购买 nike

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

为了更好地理解这个问题,我编写了一个简单的类型类 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 用于表示页面的片段,我已经为所有片段类型实现了编码器。

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")

使用的版本:

  • 斯卡拉:2.12.5
  • 无形:2.3.3

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

感谢您的帮助。

最佳答案

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

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

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

如果你真的需要保留类型参数,你要么需要使用 EncoderOps[TextFragment]::EncoderOps[NumberFragment]]HList,要么干脆去掉像这样的类型参数:

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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com