98

I'm wondering how can I use multiple type pattern matching. I have:

abstract class MyAbstract

case class MyFirst extends MyAbstract
case class MySecond extends MyAbstract
case class MyThird extends MyAbstract // shouldn't be matched and shouldn't call doSomething()

val x: MyAbstract = MyFirst

x match { 
 case a: MyFirst => doSomething()
 case b: MySecond => doSomething()
 case _ => doSomethingElse()
}

So I'd like to write something like:

x match {
 case a @ (MyFirst | MySecond) => doSomething()
 case _ => doSomethingElse()
}

I saw similar construction in some tutorial, but it gives me error:

pattern type is incompatible with expected type;
[error]  found   : object MyFirst
[error]  required: MyAbstract

So is there a way to define few different types in on case clause? I think it would make code prettier. As if I will have 5 of them, I will write same code 5 times (calling doSomething()).

starball
  • 20,030
  • 7
  • 43
  • 238
psisoyev
  • 2,118
  • 1
  • 25
  • 35
  • I think it's a XY-problem; you have a common superclass for all `doSomething` cases, why not match the `case a : MyAbstract` then...? – Patryk Ćwiek Mar 27 '13 at 09:57
  • Sorry, forgot to mention, that I have other classes, that extend MyAbstract class and shouldn't call doSomething. – psisoyev Mar 27 '13 at 09:59
  • Oh, OK, just wanted to clarify that :) You have a correct answer to your problem now though. – Patryk Ćwiek Mar 27 '13 at 10:01
  • possible duplicate of [Match multiple cases classes in scala](http://stackoverflow.com/questions/1837754/match-multiple-cases-classes-in-scala) – nawfal May 18 '13 at 10:57

1 Answers1

167

You are missing the parenthesis for your case classes. Case classes without parameter lists are deprecated.

Try this:

abstract class MyAbstract
case class MyFirst() extends MyAbstract
case class MySecond() extends MyAbstract

val x: MyAbstract = MyFirst()


x match {
   case aOrB @ (MyFirst() | MySecond()) => doSomething(aOrB)
   case _ => doSomethingElse()
}

If you have too many params for your case classes and don't like having to write long Foo(_,_,..) patterns, then maybe:

x match {
   case aOrB @ (_:MyFirst | _:MySecond) => doSomething(aOrB)
   case _ => doSomethingElse()
}

Or just:

x match {
   case _:MyFirst | _:MySecond => doSomething(x) // just use x instead of aOrB
   case _ => doSomethingElse(x)
}

But perhaps you just wanted singleton case objects?

abstract class MyAbstract
case object MyFirst extends MyAbstract
case object MySecond extends MyAbstract

val x: MyAbstract = MyFirst

x match {
   case aOrB @ (MyFirst | MySecond) => doSomething()
   case _ => doSomethingElse()
}
Faiz
  • 16,025
  • 5
  • 48
  • 37
  • 1
    And there is no way to avoid parenthesis? As I have a few parameters and it becomes ugly: case a @ (MyFirst(_, _, _, _, _) | MySecond(_, _, _, _, _)) => doSomething() – psisoyev Mar 27 '13 at 10:02
  • 10
    Did you miss `obj @ (_: MyFirst | _: MySecond)`? – Jean-Philippe Pellet Mar 27 '13 at 10:35
  • I need `obj` in cases, where I'm using it in `doSomething` call. In my case, the call of `doSomething` wasn't using `obj`, so I don't need it. But anyway, thanks for your comment! – psisoyev Mar 27 '13 at 11:24
  • @Jean-PhilippePellet Indeed I have. Let me edit my post to add it. – Faiz Mar 27 '13 at 12:08
  • What am I doing wrong here - `scala> def foo(a: Any) = a match { | case x @ (Int) => x | } foo: (a: Any)Int.type scala> val a = 100 a: Int = 100 scala> foo(a) scala.MatchError: 100 (of class java.lang.Integer) at .foo(:8)`? – Kevin Meredith Apr 07 '14 at 17:36
  • 1
    It would be nice if the compile was smart enough to find the closest common type instead of defaulting to the input type. – nilskp Sep 29 '14 at 20:03