1
votes

I'm starting to learn the great Scala language ang have a question about "deep" pattern matching

I have a simple Request class:

case class Request(method: String, path: String, version: String) {}

And a function, that tries to match an request instance and build a corresponding response:

def guessResponse(requestOrNone: Option[Request]): Response = {
    requestOrNone match {
        case Some(Request("GET", path, _)) => Response.streamFromPath(path)
        case Some(Request(_, _, _)) => new Response(405, "Method Not Allowed", requestOrNone.get)
        case None => new Response(400, "Bad Request")
    }
}

See, I use requestOrNone.get inside case statement to get the action Request object. Is it type safe, since case statement matched? I find it a bit of ugly. Is it a way to "unwrap" the Request object from Some, but still be able to match Request class fields?

What if I want a complex calculation inside a case with local variables, etc... Can I use {} blocks after case statements? I use IntelliJ Idea with official Scala plugin and it highlights my brackets, suggesting to remove them.

If that is possible, is it good practice to enclose matches in matches?

... match {
  case Some(Request("GET", path, _)) => {
      var stream = this.getStream(path)

      stream match {
        case Some(InputStream) => Response.stream(stream.get)
        case None => new Response(404, "Not Found)
      }
  }
}
2

2 Answers

2
votes

For the first part of your question, you can name the value you match against with @ :

scala> case class A(i: Int)
defined class A

scala> Option(A(1)) match {
     |   case None => A(0)
     |   case Some(a @ A(_)) => a
     | }
res0: A = A(1)

From the Scala Specifications (8.1.3 : Pattern Binders) :

A pattern binder x@p consists of a pattern variable x and a pattern p. The type of the variable x is the static type T of the pattern p. This pattern matches any value v matched by the pattern p, provided the run-time type of v is also an instance of T , and it binds the variable name to that value.

However, you do not need to in your example: since you're not matching against anything about the Request but just its presence, you could do :

case Some(req) => new Response(405, "Method Not Allowed", req)

For the second part, you can nest matches. The reason Intellij suggests removing the braces is that they are unnecessary : the keyword case is enough to know that the previous case is done.

As to whether it is a good practice, that obviously depends on the situation, but I would probably try to refactor the code into smaller blocks.

0
votes

You can rewrite the pattern as following (with alias).

case Some(req @ Request(_, _, _)) => new Response(405, "Method Not Allowed", req)

You cannot use code block in pattern, only guard (if ...).

There are pattern matching compiler plugin like rich pattern matching.