2
votes

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)))
}
2

2 Answers

3
votes

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

1
votes

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.