Search code examples
scalascala-3

Enforce type bound for inferred type parameter in pattern matching


I'm trying to compile the code below which looks correct, although the compiler refuses it. I'm probably doing something wrong or I need to hint the compiler somehow.

 sealed trait Foo[A]
 
 case class Bar[A, B <: A]() extends Foo[A]
 
 class Visitor[A](foo: Foo[A]) {
   
   def onBar[B <: A](bar: Bar[A, B]) = ???
   
   def run = foo match
     case b: Bar[A, b] => onBar[b](b)

 }

The error is

Type argument b does not conform to upper bound A

https://scastie.scala-lang.org/FLvBI6WXS1mdoS1FHhir6A


Solution

  • This issue is currently being discussed at scala/scala3#20040, and the question's code's validity will be confirmed upon resolution of that report. For the present, there exists two workarounds:

    1. If you have control of Foo and Bar, you can write a fold method to circumvent the need for a pattern match:
    sealed trait Foo[A] {
      def fold[R](caseBar: [B <: A] => Bar[A, B] => R): R
    }
    
    case class Bar[A, B <: A]() extends Foo[A] {
      override final def fold[R](caseBar: [B <: A] => Bar[A, B] => R) =
        caseBar[B](this)
    }
    
    class Visitor[A](foo: Foo[A]) {
    
      def onBar[B <: A](bar: Bar[A, B]) = ???
    
      def run = foo.fold([B <: A] => (b: Bar[A, B]) => onBar[B](b))
    
    }
    
    1. You can add an outer match layer that forces the compiler to remember B is related to A:
    sealed trait Foo[A]
    
    case class Bar[A, B <: A]() extends Foo[A]
    
    class Visitor[A](foo: Foo[A]) {
    
      def onBar[B <: A](bar: Bar[A, B]) = ???
    
      def run = foo match {
        case bar1: Bar[A, ? <: A] =>
          bar1 match {
            case bar2: Bar[A, b] =>
              onBar[b](bar2)
          }
      }
    
    }