Search code examples
scalatype-parameter

Scala: Return type of copy method when implicit evidence parameter has a representation type


Let's say I have a class with a type parameter and a method that should return a copy of the instance only if the class is parameterized with a particular trait that has a representation type. I can get that to happen pretty easily. What I can't do is put a sensible return type that method:

case class Foo[+A](a: A) {
  // Compiles
  def gotFooBar(implicit evidence: A <:< Bar[_]) = copy(a = a.Copy())

  // Does not compile
  def gotFooBar(implicit evidence: A <:< Bar[_]): Foo[A] = copy(a = a.Copy())
}

trait Bar[+B <: Bar[B]] {
  def Copy(): B // Return underlying type
}

case class Grill() extends Bar[Grill] {
  def Copy() = Grill()
}

What is the return type of that function, or perhaps more importantly, how would I set up the types so that that was the return type? Could someone also point out how the real return type could be a supertype of Foo[A]?


Solution

  • Well, since you only require Bar[_] you get Any as result of calling a.Copy. You need a type parameter for the gotFooBar method:

    case class Foo[+A](a: A) {
       def gotFooBar[B <: Bar[B]](implicit evidence: A <:< B): Foo[B] = {
          val bar = a: B
          copy(a = bar.Copy())
       }
    }
    

    The second question is, how to enforce that Foo[B] is a supertype of Foo[A]. You just need to add A as a lower bound for B:

    def gotFooBar[B >: A <: Bar[B]](implicit evidence: A <:< B): Foo[B]