Circe:将隐式编码器移动到通用 Class

Circe: Moving an Implicit Encoder into A Generic Class

我在 Circe 图书馆工作,想学习一些技巧。考虑以下代码:

import io.circe.generic.auto._
import io.circe.syntax._
import io.circe.{Decoder, Encoder, Json}

sealed trait Something
case class Name(val name: String) extends Something

class myClass[T](name: Option[Name] = None, data: Option[Seq[T]] = None) {
  // convert an arbitrary sequence to json
  def seqToJson[T](lst: Seq[T])(implicit encoder: Encoder[T]): Json = {
    lst.asJson
  }

  val mydata: Json = seqToJson(data.get)
}

object Trace extends App {
  val name = new myClass(Some(Name("Noob")))
}

继这里的优秀答案之后:,我现在正在尝试一个示例,我可以在其中将编码器构建到 class。

但是当我运行上面的代码时,它说:

could not find implicit value for parameter encoder: io.circe.Encoder[T] [error] val mydata: Json = seqToJson(data.get) [error] ^

现在我的问题是,为什么会这样。当我将隐式编码器的定义移动到 class 中时,为什么编译器无法使用它?

我也尝试了其他方法,就是移动到我定义隐式编码器的地方:

import io.circe.generic.auto._
import io.circe.syntax._
import io.circe.{Decoder, Encoder, Json}

sealed trait Something
case class Name(val name: String) extends Something

class myClass[T](name: Option[Name] = None, data: Option[Seq[T]] = None)(implicit encoder: Encoder[T]) {
  // convert an arbitrary sequence to json
  def seqToJson[T](lst: Seq[T]): Json = {
    lst.asJson
  }

  val mydata: Json = seqToJson(data.get)
}

object Trace extends App {
  val name = new myClass(Some(Name("Noob")))
}

这会产生以下错误:

找不到参数编码器的隐式值:io.circe.Encoder[Seq[T]] 不明确的隐式值:

[error]  both lazy value encodeDuration in object Encoder of type io.circe.Encoder[java.time.Duration]
[error]  and lazy value encodeInstant in object Encoder of type io.circe.Encoder[java.time.Instant]
[error]  match expected type io.circe.Encoder[T]
[error] Error occurred in an application involving default arguments.
[error]   val name = new myClass(Some(Name("Noob")))

所以我的问题是,如何将隐式定义的编码器获取到 Class 的范围内。任何对理论进行解释的答案将不胜感激!

编辑:使用 Ivan 的结构并且有效!

import io.circe.syntax._
import io.circe.{Decoder, Encoder, Json}

class myClass[T](data: Seq[T])(implicit encoder: Encoder[T]) {
  def seqToJson(lst: Seq[T]): Json = {
    lst.asJson
  }
}

object Trace extends App {
  println(new myClass[Int](data = Seq(1,2,3)))
}

Now my question is, why is this happening. When I move the definition of the implicit encoder inside the class, why can the compiler not pick up how to use it?

答案是在classmyClass的范围内没有隐含的Encoder[T]可用。

您可以通过将 (implicit encoder: Encoder[T]) 移动到构造函数来修复它,您这样做了。

此外,您在两个地方定义了通用类型 T

class myClass[T]

def seqToJson[T]

你应该只在 myClass

保留一个

could not find implicit value for parameter encoder: io.circe.Encoder[Seq[T]] ambiguous implicit values:

这是由于缺少泛型类型的问题造成的。创建 myClass 的新实例时,您没有指定类型 T。如果你这样做,它会编译。

例如,

val name = new myClass[Int](Some(Name("Noob")))

这将在运行时在线 seqToJson(data.get) 上失败,因为您没有传入任何 data.

how would I get the encoder that is defined implicitly into scope for the Class

构造函数是一个不错的选择

考虑以下简化的片段

def f[T](v: Option[T] = None) = v
f() // T is inferred as Nothing

这里类型参数T被推断为Nothing因为None被定义为

case object None extends Option[Nothing]

于是出现以下情况

def f[T](v: Option[T] = None)(implicit ev: Encoder[T]) = v
f() // error because requiring Encoder[Nothing] capability

我们实际请求Encoder[Nothing]有哪些错误。事实上,你可以通过简单地请求

来模拟类似的错误
implicitly[Encoder[Nothing]]

哪些错误

Error: diverging implicit expansion for type io.circe.Encoder[Nothing]
starting with lazy value encodeZoneOffset in object Encoder

不确定为什么它特别提到 encodeZoneOffset,但这可能与隐式搜索的工作方式有关,而 encodeZoneOffsetEncoder 同伴中的 last 隐式值。