11

Consider the following Scala code.

val a = "both"

a match {
    case "both" | "foo" => println ("foo")   // case 1
    case "both" | "bar" => println ("bar")   // case 2
}

I would like match to work so that if a == "both", Scala will execute both cases. Is this possible or are there any alternatives to achieve what I want?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jus12
  • 17,824
  • 28
  • 99
  • 157
  • possible duplicate of [Match "fallthrough": executing same piece of code for more than one case?](http://stackoverflow.com/questions/2325863/match-fallthrough-executing-same-piece-of-code-for-more-than-one-case) – nawfal May 18 '13 at 11:02

5 Answers5

25

Standard pattern-matching will always match on only exactly one case. You can get close to what you want by using the fact that patterns can be treated as partial functions (see the Language Specification, Section 8.5, Pattern Matching Anonymous Functions) and by defining your own matching operator, though:

class MatchAll[S](scrutinee : =>S) {
  def matchAll[R](patterns : PartialFunction[S,R]*) : Seq[R] = {
    val evald : S = scrutinee
    patterns.flatMap(_.lift(evald))
  }
}

implicit def anyToMatchAll[S](scrut : =>S) : MatchAll[S] = new MatchAll[S](scrut)

def testAll(x : Int) : Seq[String] = x matchAll (
  { case 2 => "two" },
  { case x if x % 2 == 0 => "even" },
  { case x if x % 2 == 1 => "neither" }
)

println(testAll(42).mkString(",")) // prints 'even'
println(testAll(2).mkString(","))  // prints 'two,even'
println(testAll(1).mkString(","))  // prints 'neither'

The syntax is slightly off the usual, but to me such a construction is still a witness to the power of Scala.

Your example is now written as:

// prints both 'foo' and 'bar'
"both" matchAll (
  { case "both" | "foo" => println("foo") },
  { case "both" | "bar" => println("bar") }
)

(Edit huynhjl pointed out that he gave a frighteningly similar answer to this question.)

Community
  • 1
  • 1
Philippe
  • 9,582
  • 4
  • 39
  • 59
  • 1
    This reminds me of http://stackoverflow.com/questions/6720205/idiomatic-way-to-convert-a-seqb/6720659#6720659. What's the benefit of the by name `=> S`? – huynhjl Sep 27 '11 at 13:35
  • 1
    Wow. I had no idea this was there. Thanks for adding the link. I use the by name argument for the (unlikely) case someone would construct the MatchAll instance before using it, so that the potential effects resulting from the evaluation of the scrutinee appear in the right place. – Philippe Sep 27 '11 at 13:46
6

At risk of being Captain Obvious, in a case like this it would be simplest just to forget pattern matching and use if.

if (a == "both" || a == "foo") println("foo")
if (a == "both" || a == "bar") println("bar") 

If the repetition of a == worries you, you could instead write

if (Set("both", "foo")(a)) println("foo")
if (Set("both", "bar")(a)) println("bar")

using the fact that the apply method on Set does the same as contains, and is a bit shorter.

Luigi Plinge
  • 50,650
  • 20
  • 113
  • 180
3

match executes one, and only one, of the cases, so you can't do this as an or in the match. You can, however, use a list and map/foreach:

val a = "both"
(a match {
  case "both" => List("foo", "bar")
  case x => List(x)
}) foreach(_ match {
  case "foo" => println("foo")
  case "bar" => println("bar")
})

And you're not duplicating any of the important code (in this case the printlns).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Matthew Farwell
  • 60,889
  • 18
  • 128
  • 171
1

Just match twice:

val a = "both"

a match {
    case "both" | "foo" => println ("foo")   // Case 1
}
a match {
    case "both" | "bar" => println ("bar")   // Case 2
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Nikita Volkov
  • 42,792
  • 11
  • 94
  • 169
0

One possible way could be:

val a = "both"

a match {
  case "foo" => println ("foo")   // Case 1
  case "bar" => println ("bar")   // Case 2
  case "both" => println ("foo"); println ("bar")
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jamil
  • 2,150
  • 1
  • 19
  • 20