This is an excerpt from the 1st Edition of the Scala Cookbook (partially modified for the internet). This is Recipe 20.4, “Scala best practice: Use match expressions and pattern matching.”
Scala Problem
Match expressions (and pattern matching) are a major feature of the Scala programming language, and you want to see examples of the many ways to use them.
Scala Solution
Match expressions (match/case statements) and pattern matching are a major feature of the Scala language. If you’re coming to Scala from Java, the most obvious uses are:
- As a replacement for the Java
switch
statement - To replace unwieldy if/then statements
However, pattern matching is so common, you’ll find that match
expressions are used in many more situations:
- In try/catch expressions
- As the body of a function or method
- With the Option/Some/None coding pattern
- In the
receive
method of actors
The following examples demonstrate these techniques.
Replacement for the Java switch
statement and unwieldy if/then statements
Recipe 3.8 showed that a match
expression can be used like a Java switch
statement:
val month = i match { case 1 => "January" case 2 => "February" // more months here ... case 11 => "November" case 12 => "December" case _ => "Invalid month" // the default, catch-all }
It can be used in the same way to replace unwieldy if/then/else statements:
i match { case 1 | 3 | 5 | 7 | 9 => println("odd") case 2 | 4 | 6 | 8 | 10 => println("even") }
These are simple uses of match
expressions, but they’re a good start.
In try/catch expressions
It helps to become comfortable with match
expressions, because you’ll use them with Scala’s try/catch syntax. The following example shows how to write a try/catch expression that returns an Option
when lines are successfully read from a file, and None
if an exception is thrown during the file-reading process:
def readTextFile(filename: String): Option[List[String]] = { try { Some(Source.fromFile(filename).getLines.toList) } catch { case e: Exception => None } }
To catch multiple exceptions in a try/catch expression, list the exception types in the catch
clause, just like a match
expression:
def readTextFile(filename: String): Option[List[String]] = { try { Some(Source.fromFile(filename).getLines.toList) } catch { case ioe: IOException => logger.error(ioe) None case fnf: FileNotFoundException => logger.error(fnf) None } }
Note that if the specific error is important in a situation like this, use the Try/Success/Failure approach to return the error information to the caller, instead of Option/Some/None. See Recipe 20.6 for both Option
and Try
examples.
As the body of a function or method
As you get comfortable with match
expressions, you’ll use them as the body of your methods, such as this method that determines whether the value it’s given is true, using the Perl definition of “true”:
def isTrue(a: Any) = a match { case 0 | "" => false case _ => true }
In general, a match
expression used as the body of a function will accept a parameter as input, match against that parameter, and then return a value:
def getClassAsString(x: Any):String = x match { case s: String => "String" case i: Int => "Int" case l: List[_] => "List" case p: Person => "Person" case Dog() => "That was a Dog" case Parrot(name) => s"That was a Parrot, name = $name" case _ => "Unknown" }
As shown in Recipe 9.8, a match
expression can also be used to create a partial function (i.e., working only for a subset of possible inputs):
val divide: PartialFunction[Int, Int] = { case d: Int if d != 0 => 42 / d }
See that recipe for more details on this approach.
Use with Option/Some/None
Match expressions work well with the Scala Option/Some/None types. For instance, given a method that returns an Option
:
def toInt(s: String): Option[Int] = { try { Some(s.toInt) } catch { case e: Exception => None } }
You can handle the result from toInt
with a match
expression:
toInt(aString) match { case Some(i) => println(i) case None => println("Error: Could not convert String to Int.") }
In a similar way, match
expressions are a popular way of handling form verifications with the Play Framework:
verifying("If age is given, it must be greater than zero", model => model.age match { case Some(age) => age < 0 case None => true } )
In actors
Match expressions are baked into actors as the way to handle incoming messages:
class SarahsBrain extends Actor { def receive = { case StartMessage => handleStartMessage case StopMessage => handleStopMessage case SetMaxWaitTime(time) => helper ! SetMaxWaitTime(time) case SetPhrasesToSpeak(phrases) => helper ! SetPhrasesToSpeak(phrases) case _ => log.info("Got something unexpected.") } // other code here ... }
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
Summary
Match expressions are an integral part of the Scala language, and as shown, they can be used in many ways. The more you use them, the more uses you’ll find for them.
See Also
- Match expressions are demonstrated in many examples in Chapter 3
- Chapter 13 demonstrates the use of
match
expressions when writing actors