Scala 尝试为ADT编写Circe编码器或解码器时遇到错误

Scala 尝试为ADT编写Circe编码器或解码器时遇到错误,scala,circe,Scala,Circe,我正试图编写一些基于的代码,但是,试图编译编码器和解码器都会导致错误 如果您想查看整个项目,可以在github()上查看 解码器 正在尝试编译以下代码: package model import java.time.LocalDateTime import cats.effect.IO import cats.syntax.functor._ import io.circe.generic.auto._ import io.circe.syntax._ import io.circe.{De

我正试图编写一些基于的代码,但是,试图编译编码器和解码器都会导致错误

如果您想查看整个项目,可以在github()上查看

解码器

正在尝试编译以下代码:

package model

import java.time.LocalDateTime

import cats.effect.IO
import cats.syntax.functor._
import io.circe.generic.auto._
import io.circe.syntax._
import io.circe.{Decoder, Encoder}
import org.http4s.EntityDecoder
import org.http4s.circe.jsonOf

package object account {

  sealed trait AccountStatus
  case object Onboarding       extends AccountStatus
  case object SubmissionFailed extends AccountStatus
  case object Submitted        extends AccountStatus
  case object AccountUpdated   extends AccountStatus
  case object ApprovalPending  extends AccountStatus
  case object Active           extends AccountStatus
  case object Rejected         extends AccountStatus

  object AccountStatus {

    implicit val accountStatusEncoder: Encoder[AccountStatus] = Encoder.instance {
      case onboarding@Onboarding => onboarding.asJson
      case submissionFailed@SubmissionFailed => submissionFailed.asJson
      case submitted@Submitted => submitted.asJson
      case accountUpdated@AccountUpdated => accountUpdated.asJson
      case approvalPending@ApprovalPending => approvalPending.asJson
      case active@Active => active.asJson
      case rejected@Rejected => rejected.asJson
    }

    implicit val accountStatusDecoder: Decoder[AccountStatus] =
      List[Decoder[AccountStatus]](
        Decoder[Onboarding].widen,
        Decoder[SubmissionFailed].widen,
        Decoder[Submitted].widen,
        Decoder[AccountUpdated].widen,
        Decoder[ApprovalPending].widen,
        Decoder[Active].widen,
        Decoder[Rejected].widen
      ).reduceLeft(_ or _)

    implicit val AccountStatusEntityDecoder = jsonOf[IO, AccountStatus]

  }


  case class Account(
                      id: String,
                      status: AccountStatus,
                      currency: String,
                      buyingPower: Double,
                      cash: Double,
                      cashWithdrawable: Double,
                      portfolioValue: Double,
                      patternDayTrader: Boolean,
                      tradingBlocked: Boolean,
                      transfersBlocked: Boolean,
                      accountBlocked: Boolean,
                      createdAt: LocalDateTime
                    )

  object Account {
    implicit val AccountDecoder: EntityDecoder[IO, Account] = jsonOf[IO, Account]
  }
}

导致以下错误:

[error] /home/tom/code/scalpaca/src/main/scala/model/account.scala:38:19: not found: type Onboarding
[error]           Decoder[Onboarding].widen,
[error]                   ^
[error] /home/tom/code/scalpaca/src/main/scala/model/account.scala:39:19: not found: type SubmissionFailed
[error]           Decoder[SubmissionFailed].widen,
[error]                   ^
[error] /home/tom/code/scalpaca/src/main/scala/model/account.scala:40:19: not found: type Submitted
[error]           Decoder[Submitted].widen,
[error]                   ^
[error] /home/tom/code/scalpaca/src/main/scala/model/account.scala:41:19: not found: type AccountUpdated
[error]           Decoder[AccountUpdated].widen,
[error]                   ^
[error] /home/tom/code/scalpaca/src/main/scala/model/account.scala:42:19: not found: type ApprovalPending
[error]           Decoder[ApprovalPending].widen,
[error]                   ^
[error] /home/tom/code/scalpaca/src/main/scala/model/account.scala:43:19: not found: type Active
[error]           Decoder[Active].widen,
[error]                   ^
[error] /home/tom/code/scalpaca/src/main/scala/model/account.scala:44:19: not found: type Rejected
[error]           Decoder[Rejected].widen
[error]                   ^
[error] 7 errors found
我发现这相当令人困惑,因为编译器抱怨的类型在范围内非常清楚,至少就我所知

编码器

删除解码器、依赖它的AccountStatusEntityDecoder以及account中的AccountStatus字段,保留以下内容

package model

import java.time.LocalDateTime

import cats.effect.IO
import cats.syntax.functor._
import io.circe.generic.auto._
import io.circe.syntax._
import io.circe.{Decoder, Encoder}
import org.http4s.EntityDecoder
import org.http4s.circe.jsonOf

package object account {

  sealed trait AccountStatus
  case object Onboarding       extends AccountStatus
  case object SubmissionFailed extends AccountStatus
  case object Submitted        extends AccountStatus
  case object AccountUpdated   extends AccountStatus
  case object ApprovalPending  extends AccountStatus
  case object Active           extends AccountStatus
  case object Rejected         extends AccountStatus

  object AccountStatus {

    implicit val accountStatusEncoder: Encoder[AccountStatus] = Encoder.instance {
      case onboarding@Onboarding => onboarding.asJson
      case submissionFailed@SubmissionFailed => submissionFailed.asJson
      case submitted@Submitted => submitted.asJson
      case accountUpdated@AccountUpdated => accountUpdated.asJson
      case approvalPending@ApprovalPending => approvalPending.asJson
      case active@Active => active.asJson
      case rejected@Rejected => rejected.asJson
    }

  }

  case class Account(
                      id: String,
                      currency: String,
                      buyingPower: Double,
                      cash: Double,
                      cashWithdrawable: Double,
                      portfolioValue: Double,
                      patternDayTrader: Boolean,
                      tradingBlocked: Boolean,
                      transfersBlocked: Boolean,
                      accountBlocked: Boolean,
                      createdAt: LocalDateTime
                    )

  object Account {
    implicit val AccountDecoder: EntityDecoder[IO, Account] = jsonOf[IO, Account]
  }

}
我再次收到一些警告和错误:

[info] Compiling 1 Scala source to /home/tom/code/scalpaca/target/scala-2.12/classes ...
[warn] /home/tom/code/scalpaca/src/main/scala/model/account.scala:27:48: match may not be exhaustive.
[warn] It would fail on the following input: Onboarding
[warn]       case onboarding@Onboarding => onboarding.asJson
[warn]                                                ^
[warn] /home/tom/code/scalpaca/src/main/scala/model/account.scala:28:66: match may not be exhaustive.
[warn] It would fail on the following input: SubmissionFailed
[warn]       case submissionFailed@SubmissionFailed => submissionFailed.asJson
[warn]                                                                  ^
[warn] /home/tom/code/scalpaca/src/main/scala/model/account.scala:29:45: match may not be exhaustive.
[warn] It would fail on the following input: Submitted
[warn]       case submitted@Submitted => submitted.asJson
[warn]                                             ^
[warn] /home/tom/code/scalpaca/src/main/scala/model/account.scala:30:60: match may not be exhaustive.
[warn] It would fail on the following input: AccountUpdated
[warn]       case accountUpdated@AccountUpdated => accountUpdated.asJson
[warn]                                                            ^
[warn] /home/tom/code/scalpaca/src/main/scala/model/account.scala:31:63: match may not be exhaustive.
[warn] It would fail on the following input: ApprovalPending
[warn]       case approvalPending@ApprovalPending => approvalPending.asJson
[warn]                                                               ^
[warn] /home/tom/code/scalpaca/src/main/scala/model/account.scala:32:36: match may not be exhaustive.
[warn] It would fail on the following input: Active
[warn]       case active@Active => active.asJson
[warn]                                    ^
[warn] /home/tom/code/scalpaca/src/main/scala/model/account.scala:33:42: match may not be exhaustive.
[warn] It would fail on the following input: Rejected
[warn]       case rejected@Rejected => rejected.asJson
[warn]                                          ^
[error] Error while emitting account.scala
[error] assertion failed: 
[error]   Cannot emit primitive conversion from Lmodel/account/package$AccountStatus; to Lmodel/account/package$Onboarding$; - account.scala
[error]      while compiling: /home/tom/code/scalpaca/src/main/scala/model/account.scala
[error]         during phase: jvm
[error]      library version: version 2.12.8
[error]     compiler version: version 2.12.8
[error]   reconstructed args: -bootclasspath /home/tom/jdk1.8.0_201/jre/lib/resources.jar:/home/tom/jdk1.8.0_201/jre/lib/rt.jar:/home/tom/jdk1.8.0_201/jre/lib/sunrsasign.jar:/home/tom/jdk1.8.0_201/jre/lib/jsse.jar:/home/tom/jdk1.8.0_201/jre/lib/jce.jar:/home/tom/jdk1.8.0_201/jre/lib/charsets.jar:/home/tom/jdk1.8.0_201/jre/lib/jfr.jar:/home/tom/jdk1.8.0_201/jre/classes:/home/tom/.ivy2/cache/org.scala-lang/scala-library/jars/scala-library-2.12.8.jar -Ypartial-unification -classpath /home/tom/code/scalpaca/target/scala-2.12/classes:/home/tom/.ivy2/cache/io.circe/circe-generic_2.12/jars/circe-generic_2.12-0.11.1.jar:/home/tom/.ivy2/cache/io.circe/circe-parser_2.12/jars/circe-parser_2.12-0.11.1.jar:/home/tom/.ivy2/cache/io.circe/circe-java8_2.12/jars/circe-java8_2.12-0.11.1.jar:/home/tom/.ivy2/cache/org.http4s/http4s-circe_2.12/jars/http4s-circe_2.12-0.20.0-M4.jar:/home/tom/.ivy2/cache/org.http4s/http4s-dsl_2.12/jars/http4s-dsl_2.12-0.20.0-M4.jar:/home/tom/.ivy2/cache/org.http4s/http4s-blaze-client_2.12/jars/http4s-blaze-client_2.12-0.20.0-M4.jar:/home/tom/.ivy2/cache/com.chuusai/shapeless_2.12/bundles/shapeless_2.12-2.3.3.jar:/home/tom/.ivy2/cache/io.circe/circe-jawn_2.12/jars/circe-jawn_2.12-0.11.1.jar:/home/tom/.ivy2/cache/org.http4s/http4s-jawn_2.12/jars/http4s-jawn_2.12-0.20.0-M4.jar:/home/tom/.ivy2/cache/org.http4s/http4s-client_2.12/jars/http4s-client_2.12-0.20.0-M4.jar:/home/tom/.ivy2/cache/org.http4s/http4s-blaze-core_2.12/jars/http4s-blaze-core_2.12-0.20.0-M4.jar:/home/tom/.ivy2/cache/io.circe/circe-core_2.12/jars/circe-core_2.12-0.11.1.jar:/home/tom/.ivy2/cache/org.typelevel/macro-compat_2.12/jars/macro-compat_2.12-1.1.1.jar:/home/tom/.ivy2/cache/org.typelevel/jawn-parser_2.12/jars/jawn-parser_2.12-0.14.1.jar:/home/tom/.ivy2/cache/org.http4s/jawn-fs2_2.12/jars/jawn-fs2_2.12-0.13.0.jar:/home/tom/.ivy2/cache/org.http4s/http4s-core_2.12/jars/http4s-core_2.12-0.20.0-M4.jar:/home/tom/.ivy2/cache/org.http4s/blaze-http_2.12/jars/blaze-http_2.12-0.14.0-M11.jar:/home/tom/.ivy2/cache/io.circe/circe-numbers_2.12/jars/circe-numbers_2.12-0.11.1.jar:/home/tom/.ivy2/cache/org.spire-math/jawn-parser_2.12/jars/jawn-parser_2.12-0.13.0.jar:/home/tom/.ivy2/cache/org.http4s/parboiled_2.12/jars/parboiled_2.12-1.0.0.jar:/home/tom/.ivy2/cache/co.fs2/fs2-io_2.12/jars/fs2-io_2.12-1.0.2.jar:/home/tom/.ivy2/cache/org.eclipse.jetty.alpn/alpn-api/jars/alpn-api-1.1.3.v20160715.jar:/home/tom/.ivy2/cache/com.twitter/hpack/jars/hpack-1.0.2.jar:/home/tom/.ivy2/cache/org.http4s/blaze-core_2.12/jars/blaze-core_2.12-0.14.0-M11.jar:/home/tom/.ivy2/cache/org.log4s/log4s_2.12/jars/log4s_2.12-1.6.1.jar:/home/tom/.ivy2/cache/co.fs2/fs2-core_2.12/jars/fs2-core_2.12-1.0.2.jar:/home/tom/.ivy2/cache/org.typelevel/cats-effect_2.12/jars/cats-effect_2.12-1.1.0.jar:/home/tom/.ivy2/cache/org.slf4j/slf4j-api/jars/slf4j-api-1.7.25.jar:/home/tom/.ivy2/cache/org.scodec/scodec-bits_2.12/jars/scodec-bits_2.12-1.1.7.jar:/home/tom/.ivy2/cache/org.typelevel/cats-core_2.12/jars/cats-core_2.12-1.5.0.jar:/home/tom/.ivy2/cache/org.typelevel/cats-kernel_2.12/jars/cats-kernel_2.12-1.5.0.jar:/home/tom/.ivy2/cache/org.typelevel/cats-macros_2.12/jars/cats-macros_2.12-1.5.0.jar:/home/tom/.ivy2/cache/org.typelevel/machinist_2.12/jars/machinist_2.12-0.6.6.jar:/home/tom/.ivy2/cache/org.scala-lang/scala-reflect/jars/scala-reflect-2.12.6.jar
[error] 
[error]   last tree to typer: TypeTree(trait Decoder)
[error]        tree position: line 53 of /home/tom/code/scalpaca/src/main/scala/model/account.scala
[error]             tree tpe: io.circe.Decoder
[error]               symbol: abstract trait Decoder in package circe
[error]    symbol definition: abstract trait Decoder extends Serializable (a ClassSymbol)
[error]       symbol package: io.circe
[error]        symbol owners: trait Decoder
[error]            call site: constructor package$Account$anon$importedDecoder$macro$28$1$anon$macro$25$1 in package account
[error] 
[error] == Source file context for tree position ==
[error] 
[error]     50                     )
[error]     51 
[error]     52   object Account {
[error]     53     implicit val AccountDecoder: EntityDecoder[IO, Account] = jsonOf[IO, Account]
[error]     54   }
[error]     55 
[error]     56 }
[error] Error while emitting account.scala
[error] assertion failed: Cannot emit primitive conversion from Lmodel/account/package$AccountStatus; to Lmodel/account/package$SubmissionFailed$; - account.scala
[error] Error while emitting account.scala
[error] assertion failed: Cannot emit primitive conversion from Lmodel/account/package$AccountStatus; to Lmodel/account/package$Submitted$; - account.scala
[error] Error while emitting account.scala
[error] assertion failed: Cannot emit primitive conversion from Lmodel/account/package$AccountStatus; to Lmodel/account/package$AccountUpdated$; - account.scala
[error] Error while emitting account.scala
[error] assertion failed: Cannot emit primitive conversion from Lmodel/account/package$AccountStatus; to Lmodel/account/package$ApprovalPending$; - account.scala
[error] Error while emitting account.scala
[error] assertion failed: Cannot emit primitive conversion from Lmodel/account/package$AccountStatus; to Lmodel/account/package$Active$; - account.scala
[error] Error while emitting account.scala
[error] assertion failed: Cannot emit primitive conversion from Lmodel/account/package$AccountStatus; to Lmodel/account/package$Rejected$; - account.scala
[warn] 7 warnings found
[error] 7 errors found
我不明白为什么这个账户会受到影响。如果我完全删除AccountStatus companion对象,我的项目将编译


我想得到一些帮助,以摆脱这些错误,您的意见仍将不胜感激。感谢

为以后可能偶然发现此问题的人提供完整的答案:

stsatlantis的建议确实解决了其中一个问题(感谢您的评论!),而另一个问题可以通过稍微修改accountStatusEncoder来解决。另一种解决方案是在ADT中使用case类,但是,如果您已经有case对象,可能是因为它们更适合您的需要/领域

我最终做出的改变是:

  object AccountStatus {

  implicit val accountStatusEncoder: Encoder[AccountStatus] =
    Encoder.instance {
      status => status match {
          case Onboarding => status.asJson
          case SubmissionFailed => status.asJson
          case Submitted => status.asJson
          case AccountUpdated => status.asJson
          case ApprovalPending => status.asJson
          case Active => status.asJson
          case Rejected => status.asJson
      }
    }

  implicit val accountStatusDecoder: Decoder[AccountStatus] =
    List[Decoder[AccountStatus]](
      Decoder[Onboarding.type].widen,
      Decoder[SubmissionFailed.type].widen,
      Decoder[Submitted.type].widen,
      Decoder[AccountUpdated.type].widen,
      Decoder[ApprovalPending.type].widen,
      Decoder[Active.type].widen,
      Decoder[Rejected.type].widen
    ).reduceLeft(_ or _)

  implicit val AccountStatusEntityDecoder = jsonOf[IO, AccountStatus]
}

您必须将
.type
添加到隐式val accountStatusDecoder中的case对象中:
解码器[AccountStatus]=List[Decoder[AccountStatus]](解码器[onboard.type]。加宽
,等等