Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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
   }
} 
like image 380
Avner Barr Avatar asked Dec 24 '22 03:12

Avner Barr


2 Answers

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.

like image 69
stefanobaghino Avatar answered Dec 25 '22 22:12

stefanobaghino


This worked for me https://scastie.scala-lang.org/pT4euWh6TFukqiuPr4T6GA:

sealed trait Sup {
  def a: String
  def i: Int
}

case class First(a: String, i: Int, d: Double) extends Sup

case class Second(a: String, i: Int, x: Seq[Double]) extends Sup

case class Third(a: String, i: Int, c: Char) extends Sup

val sups = Seq(First("s", 1, 1.0), Second("s", 4, Seq(1.1)), Third("s", 4, 'f'))

sups.foreach {
  case _: First | _: Second => println("ffff")
  case _                    => println("ggg")
}
like image 38
Yaneeve Avatar answered Dec 26 '22 00:12

Yaneeve