【问题标题】:Missing implicits when attempting to derive encoder/decoder for generic sealed trait尝试为通用密封特征派生编码器/解码器时缺少隐式
【发布时间】:2022-10-04 20:33:41
【问题描述】:

我遇到以下问题:

sealed trait Expression[T] {
  def doSomething: Either[String, T]
}

case class Literal(s: String) extends Expression[String] {
  def soSomething = Right(s)
}

object Expression{
  implicit def encoder[T: Encoder]: Encoder[Expression[T]]
  implicit def decoder[T: Decoder]: Decoder[Expression[T]]
}

我看到了几个错误:

could not find lazy implicit value of type io.circe.generic.extras.decoding.ConfiguredDecoder[Expression[T]]

not enough arguments for method deriveDecoder: (implicit decode: shapeless.Lazy[ConfiguredDecoder[Expression[T]]])

显然,我错过了一个隐含的但看不到我从我所遵循的 circe 示例中错过的内容。

任何人都可以帮助或指出更好的方法吗?

干杯

特里

编辑

下面提供的解决方案效果很好,但我正在扩展 ADT 以涵盖更多情况 - 我无法编译以下内容:

case class Equals[F[_] <: Expression[_], T](left: F[T], right: F[T]) extends Expression[Boolean]{
  def doIt: Either[String, Boolean] = ???
}

object Equals {
  implicit def encoder[F[_] <: Expression[_], T](implicit FT: encoder[F[T]]): Encoder[Equals[F, T]] = deriveEncoder
}

我怀疑我没有选择 Expression[_] 的隐式编码器我尝试在 Equals 对象中导入它,但这没有帮助。任何进一步的建议都将有助于包括如何调试这些问题的建议。

【问题讨论】:

  • 链接的帖子略有不同。我的案例类不采用类型参数,但扩展了类型化特征。
  • 是的,这也是有问题的。
  • 您的建议确实有帮助,但我现在在添加类型时遇到了进一步的问题。我已经编辑了原始帖子来描述这个案例。
  • 请看更新

标签: json scala implicit circe


【解决方案1】:

隐含的

implicit def encoder[T: Encoder]: Encoder[Expression[T]]
implicit def decoder[T: Decoder]: Decoder[Expression[T]]

意味着为了生成编解码器,Circe 必须知道Expression[T] 对任意T 有什么表示(然后为Expression[T] 生成编解码器,Circe 可以使用儿童编解码器,T 编解码器等)。代表应该是什么?

implicitly[Generic.Aux[Expression[T], ???]]

(为简单起见,我写了Generic 而不是LabelledGeneric)。

Expression[String]有一个孩子Literal,所以

implicitly[Generic.Aux[Expression[String], Literal :+: CNil]]

但是例如Expression[Int] 没有孩子

implicitly[Generic[Expression[Int]]] // doesn't compile, although I guess it could be: Generic.Aux[Expression[Int], CNil]

你只知道

implicitly[Generic.Aux[Expression[_], Literal :+: CNil]]

所以尝试无条件的隐式(如果你需要,也可以使用存在的隐式)

object Literal {
  implicit val encoder: Encoder[Literal] = deriveEncoder
  implicit val decoder: Decoder[Literal] = deriveDecoder
}

// optional, you can remove this if you don't need decode[Expression[_]]("...")
trait LowPriorityExpression {
  implicit def encoder1 : Encoder[Expression[_]] = deriveEncoder
  implicit def decoder1: Decoder[Expression[_]] = deriveDecoder
}

object Expression extends LowPriorityExpression {
  implicit def encoder: Encoder[Expression[String]] = deriveEncoder
  implicit def decoder: Decoder[Expression[String]] = deriveDecoder
}

然后

Literal("abc").asJson.noSpaces //{"s":"abc"}
(Literal("abc"): Expression[String]).asJson.noSpaces //{"Literal":{"s":"abc"}}
// (Literal("abc"): Expression[_]).asJson.noSpaces // doesn't compile without io.circe.generic.auto._

decode[Literal]("""{"s":"abc"}""") // Right(Literal(abc))
decode[Expression[String]]("""{"Literal":{"s":"abc"}}""") // Right(Literal(abc))
decode[Expression[_]]("""{"Literal":{"s":"abc"}}""") // Right(Literal(abc))
// decode[Expression[Int]]("""{"Literal":{"s":"abc"}}""") // doesn't compile, expected

也可以看看

How to use circe with generic case class that extends a sealed trait

https://github.com/circe/circe/issues/1353


我注意到使用auto insted 或semiauto Expression[T] 的编解码器可以更好地解决问题。所以我lookedreify 如何手动解析和定义这些编解码器。所以我们现在不使用auto 方法,我们正在使用semiauto 方法并在一个地方明确地重新使用一些auto 功能。

import io.circe.generic.encoding.DerivedAsObjectEncoder
import io.circe.generic.semiauto
import io.circe.generic.auto
import io.circe.generic.decoding.DerivedDecoder
import io.circe.{Decoder, Encoder}

sealed trait Expression[T] {
  def doSomething: Either[String, T]
}

case class Literal(s: String) extends Expression[String] {
  override def doSomething: Either[String, String] = Right(s)
}
object Literal {
  implicit val encoder: Encoder[Literal] = semiauto.deriveEncoder
  implicit val decoder: Decoder[Literal] = semiauto.deriveDecoder
}

case class Literal1(i: Int) extends Expression[Int] {
  override def doSomething: Either[String, Int] = Right(i)
}
object Literal1 {
  implicit val encoder: Encoder[Literal1] = semiauto.deriveEncoder
  implicit val decoder: Decoder[Literal1] = semiauto.deriveDecoder
}

case class Literal2[T](t: T) extends Expression[T] {
  override def doSomething: Either[String, T] = Right(t)
}
object Literal2 {
  implicit def encoder[T: Encoder]: Encoder[Literal2[T]] = semiauto.deriveEncoder
  implicit def decoder[T: Decoder]: Decoder[Literal2[T]] = semiauto.deriveDecoder
}

case class Equals[F[_] <: Expression[_], T](left: F[T], right: F[T]) extends Expression[Boolean] {
  override def doSomething: Either[String, Boolean] = ???
}
object Equals {
  implicit def encoder[F[_] <: Expression[_], T](implicit 
    FT: Encoder[F[T]]
  ): Encoder[Equals[F, T]] = semiauto.deriveEncoder
  implicit def decoder[F[_] <: Expression[_], T](implicit 
    FT: Decoder[F[T]]
  ): Decoder[Equals[F, T]] = semiauto.deriveDecoder
}

object Expression {
  implicit def encoder[T](implicit
    ev: DerivedAsObjectEncoder[Expression[T]]
  ): Encoder[Expression[T]] = Encoder.importedEncoder(auto.exportEncoder)
  implicit def decoder[T](implicit
    ev: DerivedDecoder[Expression[T]]
  ): Decoder[Expression[T]] = Decoder.importedDecoder(auto.exportDecoder)
}

// everything compiles
implicitly[Encoder[Literal]]
implicitly[Decoder[Literal]]
implicitly[Encoder[Literal1]]
implicitly[Decoder[Literal1]]
implicitly[Encoder[Expression[String]]]
implicitly[Encoder[Expression[Int]]]
implicitly[Decoder[Expression[String]]]
implicitly[Decoder[Expression[Int]]]
implicitly[Encoder[Equals[Expression, Int]]]
implicitly[Encoder[Equals[Expression, String]]]
implicitly[Decoder[Equals[Expression, Int]]]
implicitly[Decoder[Equals[Expression, String]]]
implicitly[Encoder[Equals[Literal2, Int]]]
implicitly[Encoder[Equals[Literal2, String]]]
implicitly[Decoder[Equals[Literal2, Int]]]
implicitly[Decoder[Equals[Literal2, String]]]

但是implicitly[Decoder[Expression[Boolean]]]implicitly[Encoder[Expression[Boolean]]] 即使使用auto 仍然无法编译。恐怕我们来到了 Scala 2 类型系统(和 Shapeless 2)的极限。

import shapeless.{Generic, :+:, CNil, Generic1, the}
implicitly[Generic.Aux[Expression[String], Literal :+: Literal2[String] :+: CNil]]
implicitly[Generic.Aux[Expression[Int], Literal1 :+: Literal2[Int] :+: CNil]]
//implicitly[Generic[Expression[Boolean]] // doesn't compile
//implicitly[Generic[Expression[_]]] // doesn't compile
  //kinds of the type arguments (F[_],T) do not conform to the expected 
  //kinds of the type parameters (type F,type T) in class Equals.
  //F[_]'s type parameters do not match type F's expected parameters:
  //type F has 1 type parameter, but type F has 1
     //https://users.scala-lang.org/t/cryptic-type-f-has-one-type-parameter-but-type-f-has-one/2776
type T
implicitly[Generic.Aux[Expression[T], Literal2[T] :+: CNil]]
trait Always[F[_]]
object Always {
  implicit def mkAlways[F[_]]: Always[F] = new Always[F] {}
}
val gen = the[Generic1[Expression, Always]]
implicitly[gen.R[T] =:= (Literal2[T] :+: CNil)]

Expression[Boolean] 的表示形式是什么?

implicitly[Generic.Aux[Expression[Boolean], ???]]

(Equals[F, _] forSome {type F[_]}) :: Literal2[Boolean] :+: CNil?

Equals[λ[T =&gt; Expression[_]], _] :: Literal2[Boolean] :+: CNil

又名Equals[({type λ[_] = Expression[_]})#λ, _] :: Literal2[Boolean] :+: CNil

在 Scala 3 中是

(Literal, Literal1, Literal2[Boolean], Equals[[_] =&gt;&gt; Expression[?], Any])

又名Literal *: Literal1 *: Literal2[Boolean] *: Equals[[_] =&gt;&gt; Expression[?], Any] *: EmptyTuple

import scala.deriving.*
val exprStrMirror = summon[Mirror.SumOf[Expression[String]]]
summon[exprStrMirror.MirroredElemTypes =:= (Literal, Literal1, Literal2[String], Equals[[_] =>> Expression[?], Any])]
val exprIntMirror = summon[Mirror.SumOf[Expression[Int]]]
summon[exprIntMirror.MirroredElemTypes =:= (Literal, Literal1, Literal2[Int], Equals[[_] =>> Expression[?], Any])]
val exprBoolMirror = summon[Mirror.SumOf[Expression[Boolean]]]
summon[exprBoolMirror.MirroredElemTypes =:= (Literal, Literal1, Literal2[Boolean], Equals[[_] =>> Expression[?], Any])]
type SumOfK1[F[_]] = Mirror.Sum { type MirroredType[T] = F[T] }
val exprMirror = summon[SumOfK1[Expression]]
summon[exprMirror.MirroredElemTypes[T] =:= (Literal, Literal1, Literal2[T], Equals[[_] =>> Expression[?], Any])]

事实上,这很有趣。如果我们至少有一个通用案例类 (Literal2[T]),那么原来的代码编译(可能我从auto 窃取的手动编解码器在某些情况下不正确而且 Circe 也不完全依赖 Shapeless 表示)

https://scastie.scala-lang.org/DmytroMitin/m7QZp29yQ3CLeQjOnm3Avw

如果我们删除通用案例类,则代码不会编译

https://scastie.scala-lang.org/DmytroMitin/m7QZp29yQ3CLeQjOnm3Avw/2

import io.circe.generic.semiauto
import io.circe.{Decoder, Encoder}

sealed trait Expression[T] {
  def doSomething: Either[String, T]
}

case class Literal(s: String) extends Expression[String] {
  override def doSomething: Either[String, String] = Right(s)
}
object Literal {
  implicit val encoder: Encoder[Literal] = semiauto.deriveEncoder
  implicit val decoder: Decoder[Literal] = semiauto.deriveDecoder
}

// !!!
case class Literal2[T](t: T) extends Expression[T] {
  override def doSomething: Either[String, T] = Right(t)
}
object Literal2 {
  implicit def encoder[T: Encoder]: Encoder[Literal2[T]] = semiauto.deriveEncoder
  implicit def decoder[T: Decoder]: Decoder[Literal2[T]] = semiauto.deriveDecoder
}

case class Equals[F[_] <: Expression[_], T](left: F[T], right: F[T]) extends Expression[Boolean] {
  override def doSomething: Either[String, Boolean] = ???
}
object Equals {
  implicit def encoder[F[_] <: Expression[_], T](implicit FT: Encoder[F[T]]): Encoder[Equals[F, T]] = semiauto.deriveEncoder
  implicit def decoder[F[_] <: Expression[_], T](implicit FT: Decoder[F[T]]): Decoder[Equals[F, T]] = semiauto.deriveDecoder
}

object Expression {
  implicit def decoder[T: Decoder]: Decoder[Expression[T]] = semiauto.deriveDecoder
  implicit def encoder[T: Encoder]: Encoder[Expression[T]] = semiauto.deriveEncoder
}

implicitly[Encoder[Literal]]
implicitly[Decoder[Literal]]
implicitly[Encoder[Expression[String]]]
implicitly[Encoder[Expression[Int]]]
implicitly[Decoder[Expression[String]]]
implicitly[Decoder[Expression[Int]]]
implicitly[Encoder[Equals[Expression, Int]]]
implicitly[Encoder[Equals[Expression, String]]]
implicitly[Decoder[Equals[Expression, Int]]]
implicitly[Decoder[Equals[Expression, String]]]
implicitly[Encoder[Equals[Literal2, Int]]]
implicitly[Encoder[Equals[Literal2, String]]]
implicitly[Decoder[Equals[Literal2, Int]]]
implicitly[Decoder[Equals[Literal2, String]]]
implicitly[Encoder[Expression[Boolean]]]
implicitly[Decoder[Expression[Boolean]]]

(我们将此代码 (*) 用于以下目的。)


我暂时删除了除一个以外的所有宏,并在打开-Ymacro-debug-lite-Xlog-implicits 时会产生macro expansion has failed: Sealed trait Expression[T] has no case class subtypes,所以显然这是一个 Circe 错误

import io.circe.generic.semiauto
import io.circe.{Decoder, Encoder}

sealed trait Expression[T] {
  def doSomething: Either[String, T]
}

case class Literal(s: String) extends Expression[String] {
  override def doSomething: Either[String, String] = Right(s)
}
object Literal {
  implicit val encoder: Encoder[Literal] = Encoder.forProduct1("s")(_.s)
  implicit val decoder: Decoder[Literal] = Decoder.forProduct1("s")(Literal.apply)
}

case class Equals[F[_] <: Expression[_], T](left: F[T], right: F[T]) extends Expression[Boolean] {
  override def doSomething: Either[String, Boolean] = ???
}
object Equals {
  implicit def encoder[F[_] <: Expression[_], T](implicit FT: Encoder[F[T]]): Encoder[Equals[F, T]] =
    Encoder.forProduct2("left", "right")(e => (e.left, e.right))
  implicit def decoder[F[_] <: Expression[_], T](implicit FT: Decoder[F[T]]): Decoder[Equals[F, T]] =
    Decoder.forProduct2("left", "right")(Equals.apply _)
}

object Expression {
  implicit def decoder[T: Decoder]: Decoder[Expression[T]] = semiauto.deriveDecoder[Expression[T]] /*!!!*/
  // implicit def encoder[T: Encoder]: Encoder[Expression[T]] = semiauto.deriveEncoder
}

其实Sealed trait Expression[T] has no case class subtypes 来自无形

sealed trait Expression[T]
case class Literal(s: String) extends Expression[String]
// case class Literal2[T](t: T) extends Expression[T]
case class Equals[F[_] <: Expression[_], T](left: F[T], right: F[T]) extends Expression[Boolean]

type T
implicitly[Generic[Expression[T]]]//macro expansion has failed: Sealed trait Expression[T] has no case class subtypes

原因是一样的

sealed trait Expression[T]
case class Literal(s: String) extends Expression[String]
case class Literal2[T](t: T) extends Expression[T]
case class Equals[F[_] <: Expression[_], T](left: F[T], right: F[T]) extends Expression[Boolean]

implicitly[Generic.Aux[Expression[String], Literal :+: Literal2[String] :+: CNil]]
// implicitly[Generic[Expression[Boolean]]] // doesn't compile, kinds of the type arguments (F[_],T) do not conform to the expected kinds of the type parameters (type F,type T) in class Equals. F[_]'s type parameters do not match type F's expected parameters: type F has 1 type parameter, but type F has 1
type T
implicitly[Generic.Aux[Expression[T], Literal2[T] :+: CNil]]

Shapeless 认为Expression[T] 的表示是Literal2[T] :+: CNil(不包括LiteralEquals)并且没有Literal2 表示是空的。

实际上,Shapeless 不将LiteralEquals 包含在Expression[T] 的表示中并不是很好。尽管上面的代码 (*) 可以编译,但它在运行时会失败(抛出异常 MatchError 或产生 Left

Literal("a").asJson.noSpaces
Literal2[Int](1).asJson.noSpaces
Equals[Literal2, Boolean](Literal2(true), Literal2(false)).asJson.noSpaces
//(Literal("a"): Expression[String]).asJson.noSpaces//MatchError
(Literal2[Int](1): Expression[Int]).asJson.noSpaces
//(Equals[Literal2, Boolean](Literal2(true), Literal2(false)): Expression[Boolean]).asJson.noSpaces//MatchError

decode[Literal]("""{"s":"a"}""")
decode[Literal2[Int]]("""{"t":1}""")
decode[Equals[Literal2, Boolean]]("""{"left":{"t":true},"right":{"t":false}}""")
decode[Expression[String]]("""{"Literal":{"s":"a"}}""")//Left, CNil should never happen
decode[Expression[Int]]("""{"Literal2":{"t":1}}""")
decode[Expression[Boolean]]("""{"Equals":{"left":{"Literal2":{"t":true}},"right":{"Literal2":{"t":false}}}}""")//Left, CNil should never happen

https://scastie.scala-lang.org/DmytroMitin/m7QZp29yQ3CLeQjOnm3Avw/4

解决方法是手动为Expression[T] 定义编解码器

implicit def decoder[T: Decoder]: Decoder[Expression[T]] = Decoder.instance {
  def f(c: HCursor): Result[Expression[T]] =
    c.get[Literal]("Literal").asInstanceOf[Result[Expression[T]]].orElse(
      c.get[Literal2[T]]("Literal2").orElse {
        def readEquals(fieldName: String): Result[Expression[T]] =
          c.downField("Equals")
           .downField(fieldName)
           .success
           .toRight(DecodingFailure(Reason.CustomReason(s"can't read Equals.$fieldName"), c))
           .flatMap(f)

        for {
          l <- readEquals("left")
          r <- readEquals("right")
        } yield new Equals(l, r).asInstanceOf[Expression[T]]
      }
    )

  f
}

implicit def encoder[T: Encoder]: Encoder[Expression[T]] = Encoder.instance {
  case a1@Literal(_) => Json.obj(("Literal", a1.asJson))
  case a1@Literal2(_) => Json.obj(("Literal2", a1.asJson))
  case Equals(l, r) => Json.obj(("Equals", 
    Json.obj(
      ("left", l.asInstanceOf[Expression[T]].asJson), 
      ("right", r.asInstanceOf[Expression[T]].asJson)
    )
  ))
}

https://scastie.scala-lang.org/DmytroMitin/m7QZp29yQ3CLeQjOnm3Avw/6

【讨论】:

    猜你喜欢
    • 2018-09-20
    • 2016-08-19
    • 2021-06-06
    • 1970-01-01
    • 2016-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多