Jack
Jack

Reputation: 16718

Combining two match patterns in one

How does one combine (in a nice way) two Scala match'es?

First I have to test if an Option is a valid value:

myOption match {
  case Some(op) =>
    doSomethingWith(op)
  case None =>
    handleTheError()

Then if op was valid, I want to test for another pattern:

Path(request.path) match {
  case "work" => {
    println("--Let's work--")

  }
  case "holiday" => {
    println("--Let's relax--")
  }
  case _ => {
    println("--Let's drink--")
  }
}

I could combine them like this:

myOption match {
  case Some(op) =>
    doSomethingWith(op)
    Path(request.path) match {
      case "work" => {
        println("--Let's work--")          
      }
      case "holiday" => {
        println("--Let's relax--")
      }
      case _ => {
        println("--Let's drink--")
      }
    }
  case None =>
    handleTheError()

But, it feels sloppy. Is there a better way to combine them in some way or another.

Update

Apologies, I should have explained better. I'm actually trying to find out if there is a known pattern for simplifying (or factoring out) these control structures. For example (imagine this was true):

x match {
 case a => {
   y match {
    case c => {}
    case d => {}
   }
 }
 case b => {}
}

equals

x -> y match {
  case a -> c => {}
  case a -> d => {}
  case b => {}
}

I was just wandering if someone has already identified some refactoring patterns for control flow, much like algebra where 2(x + y) = 2x + 2y

Upvotes: 6

Views: 2022

Answers (2)

Philippe
Philippe

Reputation: 9742

If the scrutinees (the expressions on which you match) do not depend on each other, i.e. you can compute them independently, you could perform both matches at the same time by wrapping the scrutinees in a tuple.

So your example:

x match {
 case a => {
   y match {
    case c => fooC(...)
    case d => fooD(...)
   }
 }
 case b => fooB(...)
}

could be rewritten as:

(x,y) match {
  case (a, c) => fooC(...)
  case (a, d) => fooD(...)
  case (b, _) => fooB(...)
}

...as long as y doesn't depend on x.

Upvotes: 3

4e6
4e6

Reputation: 10776

You can do

myOption map { success } getOrElse handleTheError

or with scalaz,

myOption.cata(success, handleTheError)

where success is something like

def success(op: Whatever) = {
  doSomethingWith(op)
  Path(request.path) match {
    case "work"    => println("--Let's work--")
    case "holiday" => println("--Let's relax--")
    case _         => println("--Let's drink--")      
  }
}

Update

Your pseudocode

x -> y match {
  case a -> c => {}
  case a -> d => {}
  case b => {}
}

can be literally translated to scala as

(x, y) match {
  case (a, c) => {}
  case (a, d) => {}
  case (b, _) => {}
}

It looks nice (and that's probably what you wanted) if inner matcher have only few options (c and d in this case), but it leads to code duplication (repeating of pattern a). So, in general I'd prefer map {} getOrElse {}, or separation of pattern-matchers on smaller functions. But I repeat, in your case it looks reasonable.

Upvotes: 10

Related Questions