scala 尝试派生通用密封特征的编码器/解码器时缺少隐含内容

eit6fx6z  于 2022-11-09  发布在  Scala
关注(0)|答案(1)|浏览(135)

我在以下方面遇到问题:

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
}

我怀疑我没有选择表达式[_]的隐式编码器。我曾尝试将其导入到equals对象中,但没有帮助。任何进一步的建议都将有所帮助,包括如何调试这些问题的建议。

pgx2nnw8

pgx2nnw81#

言外之意

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
我注意到,在安装了autosemiauto编解码器的情况下,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]]]

但是,即使使用autoimplicitly[Decoder[Expression[Boolean]]]implicitly[Encoder[Expression[Boolean]]]仍然不能编译。恐怕我们已经到了Scala2类型系统(和无形状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
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 => Expression[_]], _] :: Literal2[Boolean] :+: CNil
又名Equals[({type λ[_] = Expression[_]})#λ, _] :: Literal2[Boolean] :+: CNil
在Scala 3中,
(Literal, Literal1, Literal2[Boolean], Equals[[_] =>> Expression[?], Any])
又名Literal *: Literal1 *: Literal2[Boolean] *: Equals[[_] =>> 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])]

https://scastie.scala-lang.org/DmytroMitin/jrkBc5lkS1KDQO2U6uMt3Q/1
事实上,这很有趣。如果我们至少有一个通用案例类(Literal2[T]),那么原始代码就会编译(可能我从auto偷来的手动编解码器在某些情况下是不正确的,而且Circe并不完全依赖于无形状的表示)。
https://scastie.scala-lang.org/DmytroMitin/m7QZp29yQ3CLeQjOnm3Avw
如果我们删除泛型Case类,代码将无法编译
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]]

Shapless认为Expression[T]的表示为Literal2[T] :+: CNil(不包括LiteralEquals),如果没有Literal2,则表示为空。
实际上,Shapless没有将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 readExpr(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(readExpr)

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

  readExpr
}

implicit def encoder[T: Encoder]: Encoder[Expression[T]] = Encoder.instance {
  case expr@Literal(_) => Json.obj("Literal" -> expr.asJson)
  case expr@Literal2(_) => Json.obj("Literal2" -> expr.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/8
我了解如何修复运行时失败的问题(抛出MatchError或生成Left)。我们应该更换

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

使用

object Expression {
  implicit def decoder[T](implicit 
    ev: DerivedDecoder[Expression[T]]
  ): Decoder[Expression[T]] = semiauto.deriveDecoder /*ev*/
  implicit def encoder[T](implicit
    ev: DerivedAsObjectEncoder[Expression[T]]
  ): Encoder[Expression[T]] = semiauto.deriveEncoder /*ev*/
}

我刚刚将semiauto.deriveDecoder/deriveEncoder的隐式参数DerivedDecoder[Expression[T]]/DerivedAsObjectEncoder[Expression[T]]添加到def decoder[T]/def encoder[T]。以前这些隐式参数在这里被解析,在def decoder[T]def encoder[T]的定义位置,即对于一般的TExpression[T]的表示是Literal2[T] :+: CNil,这对于Literal是失败的。现在,这些隐式参数将在def decoder[T]def encoder[T]的调用点解析,即对于T=StringExpression[String]的表示将是Literal :+: Literal2[String] :+: CNil。(这类似于implicitly[X](implicit x: X)之间的差异。)
(我想,如果在Circe semiauto.deriveDecoder/deriveEncoder中是宏,让用户在必要时向方法添加隐式参数,那会更好。)
问题仍然是withtype F has 1 type parameter, but type F has 1,即Boolean外壳。
https://scastie.scala-lang.org/DmytroMitin/m7QZp29yQ3CLeQjOnm3Avw/11
我怀疑Shapless错误地计算Expression[Boolean]Generic,因此在表示Equals[_[_] <: Expression[_], _] :+: Literal2[Boolean] :+: CNilGeneric的示例中存在无效的Equals[_[_] <: Expression[_], _]

//  Generic.instance[Expression[Boolean], Equals[_[_] <: Expression[_], _] :+: Literal2[Boolean] :+: CNil](((p: Expression[Boolean]) => Coproduct.unsafeMkCoproduct(p: @_root_.scala.unchecked match {
//    case (_: Equals[_[_] <: Expression[_], _]) => 0
//    case (_: Literal2[Boolean]) => 1
//  }, p).asInstanceOf[Equals[_[_] <: Expression[_], _] :+: Literal2[Boolean] :+: CNil]), ((x1) => Coproduct.unsafeGet(x1).asInstanceOf[Expression[Boolean]]))

但即使我们修复Generic,例如,它将生成Equals2 :+: Literal2[Boolean] :+: CNil

type Equals2 = Equals[F, _] forSome {type F[_] <: Expression[_]}
// type Equals2 = Equals[Const[Expression[_]]#λ, _]

implicit val boolExprGeneric: Generic.Aux[Expression[Boolean], Equals2 :+: Literal2[Boolean] :+: CNil] =
  Generic.instance[Expression[Boolean], Equals2 :+: Literal2[Boolean] :+: CNil](
    (p: Expression[Boolean]) => Coproduct.unsafeMkCoproduct(
      (p: @unchecked) match {
        case _: Equals2           => 0
        case _: Literal2[Boolean] => 1
      },
      p
    ).asInstanceOf[Equals2 :+: Literal2[Boolean] :+: CNil],
    x => Coproduct.unsafeGet(x).asInstanceOf[Expression[Boolean]]
  )

我们将如何为存在的Equals2定义Encoder/Decoder的示例?

相关问题