Reputation: 943
I am working with the Circe library and want to learn the ropes. Consider the following code:
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")))
}
Following on from the excellent answer here: Link, I am now trying an example where I can build an encoder into a class.
However when I run the above code, it says:
could not find implicit value for parameter encoder: io.circe.Encoder[T] [error] val mydata: Json = seqToJson(data.get) [error] ^
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?
I tried something else too, which was to move where I define the implicit encoder:
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")))
}
This gives the following error:
could not find implicit value for parameter encoder: io.circe.Encoder[Seq[T]] ambiguous implicit values:
[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")))
So my question is, how would I get the encoder that is defined implicitly into scope for the Class. Any answers with an explanation of the theory would be much appreciated!
EDIT: Used Ivan's structure and it works!
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)))
}
Upvotes: 2
Views: 1552
Reputation: 48420
Consider the following simplified snippet
def f[T](v: Option[T] = None) = v
f() // T is inferred as Nothing
Here type parameter T
is inferred as Nothing
because None
is defined as
case object None extends Option[Nothing]
Hence in the following situation
def f[T](v: Option[T] = None)(implicit ev: Encoder[T]) = v
f() // error because requiring Encoder[Nothing] capability
we actually request Encoder[Nothing]
which errors. In fact, you can simulate similar error by simply requesting
implicitly[Encoder[Nothing]]
which errors with
Error: diverging implicit expansion for type io.circe.Encoder[Nothing]
starting with lazy value encodeZoneOffset in object Encoder
Not sure why specifically it mentions encodeZoneOffset
but it could be related to how implicit search works and encodeZoneOffset
is the last implicit value in Encoder
companion.
Upvotes: 1
Reputation: 7275
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?
The answer is that there is no implicit Encoder[T]
available in the scope of class myClass
.
You can fix it by moving (implicit encoder: Encoder[T])
to constructer, which you did.
Additionally, you're defining generic type T
in two places
class myClass[T]
and
def seqToJson[T]
You should keep just one at myClass
could not find implicit value for parameter encoder: io.circe.Encoder[Seq[T]] ambiguous implicit values:
This is caused by the problem of missing generic type. You didn't specify the type T
when creating a new instance of myClass
. It will compile if you do it.
For example,
val name = new myClass[Int](Some(Name("Noob")))
This will fail during runtime on line seqToJson(data.get)
because you're not passing in any data
.
how would I get the encoder that is defined implicitly into scope for the Class
Constructor is a good option
Upvotes: 3