Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Match multiple cases classes in scala

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?

like image 372
timdisney Avatar asked Dec 03 '09 05:12

timdisney


People also ask

How do you match a pattern in Scala?

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.

How is case class used in pattern matching?

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.

Does Scala have pattern matching?

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.

How do you write a match case in Scala?

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...") }


2 Answers

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"   } } 
like image 151
Mitch Blevins Avatar answered Oct 21 '22 05:10

Mitch Blevins


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.

like image 40
Don Mackenzie Avatar answered Oct 21 '22 05:10

Don Mackenzie