scalagenericspattern-matchingscala-2

How can I return a subclass of a type from a method with a generic, bounded type?


Given the below example, how can I make the code compile without resorting to casting in myMethod?

object Test {
  sealed trait MyTrait
  case class A(v: Int) extends MyTrait
  case class B(v: Int) extends MyTrait

  def myMethod[T <: MyTrait](in0: T): T = {
    in0 match {
      case in: A => in.copy(v = 1)
      case in: B => in.copy(v = 2)
    }
  }

}

Thanks.


Solution

  • You can try to:

    For instance you could implement it like this

    import scala.annotation.nowarn
    
    sealed trait MyTrait
    case class A(v: Int) extends MyTrait
    case class B(v: Int) extends MyTrait
    
    @nowarn
    def myMethod[T <: MyTrait](in0: T): T = {
      case class IntoT[A, B](value: A, upcast: A => B)
      IntoT(in0, identity[T]) match {
        case IntoT(in : A, upcastA: (A => T)) => upcastA(in.copy(v = 1))
        case IntoT(in : B, upcastB: (B => T)) => upcastB(in.copy(v = 2))
      }
    }
    

    It will make mismatching types error go away. Instead you will get warning that

    match may not be exhaustive.
    
    It would fail on pattern case: IntoT(_, _)
    

    which in turn could be suppressed with @nowarn.

    However, to be honest in such cases when I know that I know better than the compiler - because code is trivial and obvious at a glance, and I also cover it with tests - .asInstanceOf is not that bad. Sometimes you cannot avoid it so it's good to create some small, well tested utility which will help to deal with all cases where compiler was not so smart.