0
votes

I am slowly teaching myself Scala type classes. Suppose I have the following example:

object Example extends App {

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

  object Serializer {
    def serialize[T](seq: Seq[T])(implicit serializer: Serializer[T]): Value = serializer.serialize(seq)
  }

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

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

  case class Data[T: Serializer](x: Seq[T], y: Seq[T], z: Seq[T]) {
    val series = Data.createString(x, y, z)
  }

  object Data {
    def createString[T : Serializer](x: Seq[T], y: Seq[T], z: Seq[T]) = {
      val serialize = implicitly[Serializer[T]]
      List(serialize.serialize(x), serialize.serialize(y))
    }
  }

  val x = Seq("a", "b", "c")
  val y = Seq(1, 2, 3, 4)
  val z = Seq(10, 20, 30)
  val data = Data(x, y, z)
  println(data.series)
}

Now this fails with

could not find implicit value for evidence parameter of type Example.Serializer[Any] [error] val data = Data(x, y, z)

Can someone please guide me as to how I can make createString method work? From my understanding because I have a Seq[Int] and a Seq[String], the type [T] will get inferred as [Any], which causes the issue.

However, I put a context bound based on my Serializer type class, which I thought would make the compiler look for a String or Int serializer, which it fails to do. I basically want to pass in any sequence for which I have a defined serializer, so in this any combination of Seq[Int] and Seq[String].

Any guidance would be much appreciated!

2

2 Answers

3
votes

Again the problem is that you want different types, but your are using a single type parameter T; so the compiler infers any.
Maybe you want to define your class like this:

final case class Data[A: Serializer, B: Serializer, C: Serializer](x: Seq[A], y: Seq[B], z: Seq[C])

So you can have the three different types.


BTW, I would suggest you to use List or Vector or ArraySeq, i.e. concrete collections, instead of the abstract Seq.

2
votes

If you want fields of Data to be Seqs of different element types then do this

case class Data[T: Serializer, T1: Serializer, T2: Serializer](x: Seq[T], y: Seq[T1], z: Seq[T2]) {
  val series = Data.createString(x, y, z)
}

object Data {
  def createString[T : Serializer, T1 : Serializer, T2 : Serializer](x: Seq[T], y: Seq[T1], z: Seq[T2]) = {
    val serialize = implicitly[Serializer[T]]
    val serialize1 = implicitly[Serializer[T1]]
    val serialize2 = implicitly[Serializer[T2]]
    List(serialize.serialize(x), serialize1.serialize(y), serialize2.serialize(z))
  }
}