scalaimplicittype-erasureclasstag

type is erased from implicit class


I'm having a implicit class to add a certain function for a case class, for example:

case class TestClass(name:String)

implicit class ChangeProduct[S <: Seq[T], T <: Product](seq: Option[Seq[T]]) {
    def convert(expr: T => T): Option[Seq[T]] = seq.map(_.map(expr))
}

val c = Option(List(TestClass("a"), TestClass("b")))
val r = c.convert(p => p.copy(name = p.name.toUpperCase()))
println(r)

I'm happy to see the output is

Some(List(TestClass(A), TestClass(B)))

But now I try to make the implicit class more generic by change its parameter to seq:Option[S]:

implicit class ChangeProduct[S <: Seq[T], T <: Product](seq: Option[S]) {
    def convert(expr: T => T): Option[S] = seq.map(_.map(expr))
}

val c = Option(List(TestClass("a"), TestClass("b")))
val r = c.convert(p => p.copy(name = p.name.toUpperCase()))
println(r)

Unfortunately I got error message:

Error:(37, 51) type mismatch;
   found   : Seq[T]
   required: S
        def convert(expr: T => T): Option[S] = seq.map(_.map(expr))

And for expression p.copy(name = p.name.toUpperCase()), it said

Type mismatch.
    Required: Nothing => Nothing
    Found : Nothing => Any

I think it might be type erasure problem, but I don't know how to fix it.


Solution

  • All right, I figured it out. If I do want limit the S be sub type of Seq, I should give it an explicit parameter instead of underscore. And also because I have T <: Product, the U must be covariant:

    implicit class ChangeProduct[S[+U] <: Seq[U], T <: Product](seq: Option[S[T]]) {
        def convert(expr: T => T): Option[Seq[T]] = seq.map(_.map(expr))
    }
    
    val c = Option(List(TestClass("a"), TestClass("b")))
    val r = c.convert(p => p.copy(name = p.name.toUpperCase()))
    println(r)
    

    Thanks @Mario Galic and @Alexey Romanov