Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lots of nested match ... case in pattern matching

Tags:

scala

I have an automatically generated client for an web service. I has many complicated classes and I have to do a pattern matching against it. For now I have a structure looking like this:

val response = client.getResponse
response match {
  case Left(_) => None
  case Right(a: SomeClass) => a match {

    case SomeClass2(b: Option[SomeClass3]) => b match {

      case None => None
      case Some(c: SomeClass3) => c match {

        case SomeClass4(_, _, _, _, d: Seq[SomeClass4]) => d match {
          case Nil => None

          case seq: Seq[SomeClass5] => seq match {
            case Nil => None
            case Seq(xs@_*) => xs map { x =>
              x match {
                case Nil => None

                case SomeClass6(e: SomeClass7) => e match {
                  case Nil => None

                   case SomeClass8(f, _, _, _, _) => f match {
                    case Nil => None
                    case Seq(xs@_*) => xs map { x => 
                      x match {

                        case Nil => None
                        case SomeClass9(g: Seq[SomeClass9], _, _, _, _, _, _, _, _, _, _) => /* + some nested levels more*/
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

where SomeClass1 - SomeClass9 are case classes.

As you can see, it seems frightening. What do I do about it? What's the standard way to make it look nicer?

I guess there should be not only refactoring but rather another approach.

like image 235
Alan Coromano Avatar asked Sep 17 '13 03:09

Alan Coromano


People also ask

Which method of case class allows using objects in pattern matching?

The match method takes a number of cases as an argument. Each alternative takes a pattern and one or more expressions that will be performed if the pattern matches.

How does pattern matching work?

Pattern Matching works by "reading" through text strings to match patterns that are defined using Pattern Matching Expressions, also known as Regular Expressions. Pattern Matching can be used in Identification as well as in Pre-Classification Processing, Page Processing, or Storage Processing.

What is case _ in Scala?

case _ => does not check for the type, so it would match anything (similar to default in Java). case _ : ByteType matches only an instance of ByteType . It is the same like case x : ByteType , just without binding the casted matched object to a name x .

Which symbol is used for the default condition in match expression in Scala?

Explanation: In the above program, if the value of x which is passed in test method call matches any of the cases, the expression within that case is evaluated. Here we are passing 1 so case 1 will be evaluated. case_ => is the default case which will get executed if value of x is not 0 or 1.


2 Answers

Assuming that a should be SomeClass2, but not SomeClass (same with b, c, d).

You could use alternative patterns like case A | B => ... and structural patterns like Some(MyClass(f)).

Also you could use partial function in map like map { case ... } instead of map { x => x match {...} }.

And I guess there is a error in your code: there is check for case Nil => ...; case SomeClass8(...) => ....

You could replace Seq(xs @_*) with xs. If you need entire collection you don't need to extract elements.

Your code:

response match {
  case Left(_) | Right(SomeClass2(None)) | Right(SomeClass2(Some(SomeClass3(_, _, _, _, Nil))) => None
  case Right(SomeClass2(Some(SomeClass3(_, _, _, _, xs))) =>
    xs map {
      case SomeClass6(None) | SomeClass6(Some(SomeClass8(Nil, _, _, _, _))) => None
      case SomeClass6(Some(SomeClass8(xs, _, _, _, _))) =>
        xs map {
          case Nil => None
          case SomeClass9(g, _, _, _, _, _, _, _, _, _, _) => /* + some nested levels more*/
        }
    }
}

You should also extract nested matches to separate methods.

Pattern matching is not the only solution. You could use methods of Either and Option:

response.right.toOption.collect {
  // No need for the first part.
  case SomeClass2(Some(SomeClass3(_, _, _, _, xs)) if xs.nonEmpty => ...
}
like image 184
senia Avatar answered Sep 22 '22 05:09

senia


You might find extractors useful. It might also be worth flattening some of the cases so you have

case Right(SomeClass(SomeClass2(Some(SomeClass3(value))))) => value ...
case _ => None

rather than having a None case explicitly defined at each level.

like image 24
ThinTim Avatar answered Sep 21 '22 05:09

ThinTim