I'm doing matching against some case classes and would like to handle two of the cases in the same way. Something like this:
abstract class Foo case class A extends Foo case class B(s:String) extends Foo case class C(s:String) extends Foo def matcher(l: Foo): String = { l match { case A() => "A" case B(sb) | C(sc) => "B" case _ => "default" } }
But when I do this I get the error:
(fragment of test.scala):10: error: illegal variable in pattern alternative case B(sb) | C(sc) => "B"
I can get it working of I remove the parameters from the definition of B and C but how can I match with the params?
A pattern match includes a sequence of alternatives, each starting with the keyword case. Each alternative includes a pattern and one or more expressions, which will be evaluated if the pattern matches. An arrow symbol => separates the pattern from the expressions.
Case classes help us use the power of inheritance to perform pattern matching. The case classes extend a common abstract class. The match expression then evaluates a reference of the abstract class against each pattern expressed by each case class.
Notes. Scala's pattern matching statement is most useful for matching on algebraic types expressed via case classes. Scala also allows the definition of patterns independently of case classes, using unapply methods in extractor objects.
First, another example of how to match ranges of numbers: i match { case a if 0 to 9 contains a => println("0-9 range: " + a) case b if 10 to 19 contains b => println("10-19 range: " + a) case c if 20 to 29 contains c => println("20-29 range: " + a) case _ => println("Hmmm...") }
Looks like you don't care about the values of the String parameters, and want to treat B and C the same, so:
def matcher(l: Foo): String = { l match { case A() => "A" case B(_) | C(_) => "B" case _ => "default" } }
If you must, must, must extract the parameter and treat them in the same code block, you could:
def matcher(l: Foo): String = { l match { case A() => "A" case bOrC @ (B(_) | C(_)) => { val s = bOrC.asInstanceOf[{def s: String}].s // ugly, ugly "B(" + s + ")" } case _ => "default" } }
Though I feel it would be much cleaner to factor that out into a method:
def doB(s: String) = { "B(" + s + ")" } def matcher(l: Foo): String = { l match { case A() => "A" case B(s) => doB(s) case C(s) => doB(s) case _ => "default" } }
There are a couple of ways that I can see to achieve what you are after, if you have some commonality between case classes. The first is to have the case classes extend a trait which declares the commonality, the second is to use a structural type which removes the need to extend your case classes.
object MuliCase { abstract class Foo case object A extends Foo trait SupportsS {val s: String} type Stype = Foo {val s: String} case class B(s:String) extends Foo case class C(s:String) extends Foo case class D(s:String) extends Foo with SupportsS case class E(s:String) extends Foo with SupportsS def matcher1(l: Foo): String = { l match { case A => "A" case s: Stype => println(s.s); "B" case _ => "default" } } def matcher2(l: Foo): String = { l match { case A => "A" case s: SupportsS => println(s.s); "B" case _ => "default" } } def main(args: Array[String]) { val a = A val b = B("B's s value") val c = C("C's s value") println(matcher1(a)) println(matcher1(b)) println(matcher1(c)) val d = D("D's s value") val e = E("E's s value") println(matcher2(d)) println(matcher2(e)) } }
The structural type method generates a warning about erasure which, at present I'm not sure how to eliminate.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With