0

I am going through some exercises I have invented on case classes and typeclasses. One of the problems I have faced is the following:

object Example extends App {

  sealed trait Serializer[T] {
    def serialize(seq: List[T]): String
  }

  implicit object StringSerializer extends Serializer[String] {
    def serialize(seq: List[String]): String = seq.toString()
  }

  implicit object IntSerializer extends Serializer[Int] {
    def serialize(seq: List[Int]): String = seq.toString()
  }

  case class Marker[T: Serializer](lst: Option[List[T]] = None)
  
  Marker() // ambiguous implicit values: here...
}

Now this gives an error about ambiguous implicit values. I think this is related to a question I have asked before (albeit a different error message):

Type erasure in a nested list with a given context bound

Am I correct in that it is the same process at work here, even though the error message is different?

finite_diffidence
  • 893
  • 2
  • 11
  • 20
  • 1
    What do you want your Marker instance to be for, Int or List? – user Jul 01 '20 at 14:48
  • I was hoping that marker could be either Int or List, and only when a user passes in a list would the type be set. In my mind I assumed the compiler would just not bother when the option was equal to None. – finite_diffidence Jul 01 '20 at 14:52
  • 2
    Well, the `lst` field is not mutable, so there was no way anyone could pass a different list in later – user Jul 01 '20 at 14:53

1 Answers1

5

Compiler can't infer T. Try to specify T explicitly

Marker[String]() // compiles
Marker[Int]() // compiles

When you provide lst it can infer T itself

Marker(Some(List(1, 2)))
Marker(Some(List("a", "b")))

For the same reason

Marker(Option.empty[List[Int]])
Marker(Option.empty[List[String]])
Marker[Int](None)
Marker[String](None)
Marker(None: Option[List[Int]])
Marker(None: Option[List[String]])

compile while Marker(None) doesn't.

Alternatively you can prioritize your implicits

trait LowPrioritySerializer {
  implicit object StringSerializer extends Serializer[String] {
    def serialize(seq: List[String]): String = seq.toString()
  }
}

object Serializer extends LowPrioritySerializer {
  implicit object IntSerializer extends Serializer[Int] {
    def serialize(seq: List[Int]): String = seq.toString()
  }
}

Then IntSerializer will be tried firstly and StringSerializer will be tried secondly if IntSerializer didn't work (if type is different).

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66