Search code examples
scalasyntaxpattern-matchingcase-class

scala syntax to match on multiple case class types without decomposing the case class


I have a sealed trait with various case class implementations. I want to pattern match on multiple classes at once for the same match expression. I can't seem to do it without decomposing the case classes and "|" between them

currently looks like:

sealed trait MyTrait {
  val param1: String
  ...
  val param100: String
}

case class FirstCase(param1: String ...... param100: String) extends MyTrait
...
case class NthCase(param1: String ..... param100: String) extends MyTrait

another place in code:

def myFunction(something: MyTrait) = {
   ...
   val matchedThing = something match {
      // this doesn't work with "|" character
      case thing: FirstCase | SecondCase => thing.param1
      ...
      case thing: XthCase | JthCase => thing.param10
   }
} 

Solution

  • Let's go there step by step:

    1. The | operator, in the context of pattern matching, allows you to define alternative patterns, in the following form:

      pattern1 | pattern2
      
    2. If you want to define a pattern that matches a type, the pattern must be provided in the following form:

      binding: Type
      
    3. Providing a choice between two different types should then be provided in the following form:

      binding1: Type1 | binding2: Type2
      
    4. To bind a single name to the two alternative bindings you can discard the name of the individual bindings (using the _ wildcard) and bind the name of the overall pattern to another binding using the @ operator, as expressed in the following example:

      binding @ (_ : Type1 | _ : Type2)
      

    The following is an example:

    sealed trait Trait {
      def a: String
      def b: String
    }
    
    final case class C1(a: String, b: String) extends Trait
    final case class C2(a: String, b: String) extends Trait
    final case class C3(a: String, b: String) extends Trait
    
    object Trait {
      def f(t: Trait): String =
       t match {
        case x @ (_ : C1 | _ : C2) => x.a // the line you are probably interested in
        case y: C3 => y.b
      }
    }
    

    Here is some sample output on invoking f:

    scala> Trait.f(C1("hello", "world"))
    res0: String = hello
    
    scala> Trait.f(C2("hello", "world"))
    res1: String = hello
    
    scala> Trait.f(C3("hello", "world"))
    res2: String = world
    

    You can play around with the presented examples here on Scastie.