Published 2014-05-01.
Last modified 2015-09-22.
Time to read: 8 minutes.
Pattern matching is a core strength of Scala. Although simple in concept, it may take a while for you to readjust how you think about programming once you realize how powerful pattern matching is. The Sealed Classes and Extractors lecture builds on this lecture.
Scala can match on values, types, and a combination of types and values with conditional tests.
The Scala 2 sample code for this lecture can be found in
courseNotes/
.
Multiway Branch/Match on Value
Scala has a multi-way branch, just like many other languages.
Scala’s match
expression is similar to a switch
statement in other languages.
You can match on any object, including numbers and Strings.
Because every Scala expression returns a value, the values returned by each of the cases of a match expression should be of the same type, or the Scala compiler will widen the return type of the entire match expression. We discussed this in the Type Hierarchy and Equality lecture. The cases are tested in the order written.
The matchOnValue
methods accept a String
parameter called x
and returns an Int
.
The syntax for Scala 2 and Scala 3 differs:
Scala 2 requires braces around the case
expressions, which Scala 3 does not allow braces.
scala> def matchOnValue(x: String): Int = x match { case "a" => 1 case _ => 0 // default case matches everything } matchOnValue: (x: String)Int
scala> def matchOnValue(x: String): Int = x match case "a" => 1 case _ => 0 // default case matches everything matchOnValue: (x: String)Int

In both versions of the above code,
the value of x
is matched against the literal string "a"
,
and if they are equal then the value returned by the match expression is the integer 1.
Otherwise, the next match value, which is a wild card,
specified with an underscore, matches every possible value and discards the matched value.
It is a good practice to ensure that every possible match is provided for. Wildcards are useful as catch-all clauses. As you already know, Scala uses underscores for many purposes, mostly as a wildcard or a placeholder. Let’s try this in the REPL; the method invocations are the same for Scala 2 and 3:
scala> matchOnValue("a") res0: Int = 1
scala> matchOnValue("q") res2: Int = 0
The matchOnValue2
method uses a capturing variable called y
instead of a variable called _
.
When written this way, y
has no type or value constraints.
The benefit of providing a variable name instead of an underscore is so the matched value can be referred to in the body of the case clause.
I wrote a version for Scala 2 and a version for Scala 3:
scala> def matchOnValue2(x: String): Int = x match { case "a" => 1 case y => if (y.isEmpty) 0 else y.charAt(0).toInt } matchOnValue2: (x: String)Int
scala> def matchOnValue2(x: String): Int = x match case "a" => 1 case y => if (y.isEmpty) 0 else y.charAt(0).toInt matchOnValue2: (x: String)Int
The highlighted default case above matches every possible value.
Instead of using an unnamed variable (_
) as a wildcard, a variable named y
is used as a wildcard.
Notice that the body of the default case contains a conditional test based on the value of y
.
If y is the empty string, then the value 0 is returned,
otherwise the numeric value of the first character in the string called y
is returned.
Let’s run this code:
scala> matchOnValue2("x") res3: Int = 120
scala> matchOnValue2("a") res4: Int = 1
scala> matchOnValue2("") res5: Int = 0
This is not the best writing style, and we will see a better way of writing this using a guard next.
You can run this code by typing:
$ sbt "runMain PatMatch1" matchOnValue("a")=1 matchOnValue("q")=0 matchOnValue2("a")=1 matchOnValue2("q")=113
Providing a Guard

Scala’s match expression can test against a condition with an if
clause (notice that the test predicate is not enclosed in
parentheses).
This type of test predicate is often referred to as a guard.
Let’s rework matchOnValue2
so it uses a guard.
The value of x
is first compared to the literal string "a"
; if there is a match, the integer value 1 is returned
and the case clause is complete.
Otherwise, if y
is the empty string, the value zero is returned and the case clause is complete.
Finally, the catch-all clause has no restrictions; all values are unconditionally accepted by this clause, and since we know that
y
does not contain the empty string, the value of its first character is converted to an integer and that becomes the value of the
entire match expression.
def matchOnValue3(x: String): Int =
x match {
case "a" => 1
case y if y.isEmpty => 0
case y => y.charAt(0).toInt // this is the catch-all default case
}
The results are the same as before with matchOnValue2
.
I think a writing style that uses guards is easier to read.
That said, here is an even better way of writing this expression:
def matchOnValue4(x: String): Int = x match { case "a" => 1 case "" => 0 case y => y.charAt(0).toInt // this is the catch-all default case }
You can run this code by typing.
$ sbt "runMain PatMatch2"
Matching Against Multiple Values
A match expression can match against multiple values at once.
Simply separate each value with a vertical bar (think of a vertical bar as meaning ’or’).
For this example, the second case will be activated if the value of x
is 2, or 4 or 6.
scala> def wat(x: Int): Unit = x match { | case 1 => println("x=1") | case 2 | 4 | 6 => println("2, 4 or 6") | case _ => println("something else") | } wat: (x: Int)Unit
scala> wat(1) x=1
scala> wat(2) 2, 4 or 6
scala> wat(3) something else
scala> wat(4) 2, 4 or 6
Matching On Type

Let’s consider how to match according to an expression’s type instead of its value; this goes beyond switch statements in other languages.
To do this, let’s define a method called whatever
that can return type Any
.
It is actually written to either return an Int
or a String
, depending on whether the current system time has an odd or an
even number of milliseconds when the whatever
method was invoked.
I explicitly declared the return type as Any
, which is actually the type returned by the if
expression in the body of the
whatever
method.
Each time you run the method there is a 50% chance of getting the integer value 1
returned and there is a 50% chance of the string
"blah"
being returned.
scala> def whatever: Any = if (System.currentTimeMillis % 2 == 0) 1 else "blah" whatever: Any
scala> whatever match { | case a: Int => println("no") | case b: String => println(b) | } no
This match
expression selects the appropriate case
clause according to the type returned by the whatever
method; this match
expression does not consider values when selecting the matching case
clause.
If the returned type from whatever
is an Int
, the first case
clause is matched, which causes the matched
value to be stored into a new immutable variable called a
, then "no"
is printed and the returned value from
println
(which is Unit
) is returned as the value of the entire match
expression.
If the type returned by the whatever
method was instead a String
, the second case
clause will be selected,
which will cause the value of the matched String
to be stored in a new immutable variable called b
, and that value will be
printed.
Again, the returned value from the selected case
clause and therefore the entire match expression is Unit
.
Immutable variables a
and b
are local to their respective case clauses.
The above match
expression does not match every possibility; the whatever
method is declared to return Any
,
and according to that definition there are an infinite number of possible types that it might return.
Attempting to match against any other type will cause a runtime error, as we shall see shortly.
Let’s see what happens if whatever
returns a different type, such as a Boolean
.
Our first attempt at writing this code does not compile.
false match {
case a: Int => println(s"Whatever: Int with value $a")
case b: String => println(s"Whatever: String with value ’$b’")
}
The compiler error message for the above is: scrutinee is incompatible with pattern type
, which is itself an inscrutible error message.
It just means that the type being matched has no matching case
clause.
We can make this code compile by widening the type being compared by writing it this way.
(false: Any) match {
case a: Int if a<3 => println(s"$a is an integer less than 3")
case b: Int => println(s"$b is an integer greater or equal to 3")
}
However, when you run the above you will get scala.MatchError: false (of class java.lang.Boolean)
.
This is because there was no case
clause that specified a Boolean
type, or the value false
, or a default
case
.
If the case
clause has no type specification then it matches every type.
Similarly, if the case
clause has no value specification then it will match all values.
Thus, a case
clause without a value specification or a type specification or a guard should be the last case to be matched, because
it is guaranteed to successfully match everything.
As we saw earlier, if the default case clause has a named variable then the value being matched will be stored into that variable, otherwise if
the default case
clause uses an underscore, then the matched value is not available to the body of the case
clause.
Let’s provide a default case
that handles unexpected match types by not specifying a match type or a match value:
(false: Any) match { case a: Int => println("no") case b: String => println(b) case c => println(s"$c has an unexpected type") // default case matches on all values and all types }
Providing a default case for match
is important, unless we know that all possible cases have been provided.
We will discuss sealed
classes and traits in the
Sealed Classes and Extractors lecture,
which allows us to ensure that all possible cases have been considered.
Let’s add a default case
to the previous match
on whatever
’s return value:
whatever match { case a: Int if a<3 => println(s"$a is an integer less than 3") case b: Int => println(s"$b is an integer greater or equal to 3") case x => println(s"$x is not an integer") }
You can run this code by typing.
$ sbt "runMain PatMatch3"
Output might be something like the following, depending on the random values and types generated.
whatever: String with value ’blah’ false is not an integer 1 is an integer less than 3
Example: UnWrapping Java nulls Wrapped with Option

We saw in the Option, Some and None lecture that we could use
get
, getOrElse
and orElse
to pull out the value that might be contained within an Option
.
You can also use match
to extract a value from Option
.
As an example, the maybeSystemProperty
method accepts the name of a system property and either returns a String
containing the name and value of the system property, or a default message if the property is not defined.
def maybeSystemProperty(name: String): String = Option(System.getProperty(name)) match { case Some(value) => s"Property ’$name’ value=’$value’" // value is extracted from the Option case None => s"Property ’$name’ is not defined" }
The method works by wrapping the result of looking up the value of the given property name in an Option
.
If the system property is defined, its value is returned, wrapped in an instance of Some
, otherwise if the system property is
not defined, None
is returned.
This Option
value (a Some
instance or None
) is then provided to the match
.
Two cases are provided for: the Some
case and the None
case.
The way the first case works may surprise you: a new immutable variable is created called value
, and its scope is restricted to
the first case.
It does not need parentheses around it, but you could write them if that helps you understand.
BTW, many Scala beginners actually write these parentheses, but they are unnecessary.
If the Option
being matched against is actually an instance of Some
, its value is extracted and that becomes the
value of the value
variable.
The Unapply lecture will talk about how the extraction process works.
If the Some
case did not match, the value
variable is discarded and the next case is considered.
Because Option
can only have one of two possible subtypes (Some
or None
), the None
case
is guaranteed to match if the Some
case did not match.
Notice that the None
case has no variable name and no underscore.
This is legal, and it means that the value of None
is not captured.
This means that a match
expression can do more than just match values and types: it can also extract values and process or
compare them during or after the matching process.
Let’s try this out.
As you can see, os.name
is a predefined Java system property, and there is no system property called a
.
scala> maybeSystemProperty("os.name") res3: String = Property ’os.name’ value=’Linux’
scala> maybeSystemProperty("a") res4: String = Property ’a’ is not defined
You can run this code by typing.
$ sbt "runMain PatMatch4" maybeSystemProperty("os.name")=Property ’os.name’ value=’Linux’ maybeSystemProperty("a")=Property ’a’ is not defined
Matching On Type With a Guard
Let’s modify a previous example to use a guard in a match on type. Notice that the cases are ordered such that they are progressively less restrictive.
whatever match {
case x: Int if x<3 => println(s"$x is an integer less than 3")
case x: Int => println(s"$x is an integer greater or equal to 3")
case x => println(s"$x is not an integer")
}
We can package the above into a method called guardedMatch
.
Notice that guardedMatch
accepts Any
type.
def guardedMatch(value: Any): String = value match {
case x: Int if x<3 => s"$x is an integer less than 3"
case x: Int => s"$x is an integer greater or equal to 3"
case _ => "Did not get an integer" // catch-all case
}
Let’s try it out:
scala> guardedMatch(0) res3: String = 0 is an integer less than 3
scala> guardedMatch(99) res4: String = 99 is an integer greater or equal to 3
scala> guardedMatch("blah") res5: String = Did not get an integer
You can run this code by typing.
$ sbt "runMain PatMatch5" guardedTypeMatch(0)=0 is an integer less than 3 guardedTypeMatch(99)=99 is an integer greater or equal to 3 guardedTypeMatch("blah")=Did not get an integer
Another Example

This example demonstrates guards with a match on type.
We worked with the Animal
, Frog
and Dog
classes in the earlier
Type Hierarchy and Equality,
Classes Part 1,
Classes Part 2 and
Case Classes lectures.
abstract class Animal(numLegs: Int, breathesAir: Boolean) { private val breatheMsg = if (breathesAir) "" else " do not" val msg = s"I have $numLegs legs and I $breatheMsg breathe air" }
case class Frog(canSwim: Boolean, numLegs: Int, breathesAir: Boolean) extends Animal(numLegs, breathesAir)
case class Dog(barksTooMuch: Boolean) extends Animal(4, true)
Now we can figure out what kind of Animal
we have.
match
works by successively comparing the animal type against all the cases, in order.
You can think of each case
as a list of filters, from most specific to least specific.
Notice that the first match case
checks the Frog
’s numLegs
value; this class property is
available only if the match on type was successful.
Also notice that the last match (x
) does not specify a value, type or guard – by now you should know that this means that
this case
will always match and so acts as the default case
.
def classify(animal: Animal): String = animal match { case frog: Frog if frog.numLegs>0 => s"Got a Frog with ${frog.numLegs} legs; canSwim=${frog.canSwim} and breathesAir=${frog.breathesAir}"
case tadpole: Frog => s"Got a tadpole without legs; breathesAir=${tadpole.breathesAir}"
case dog: Dog if dog.barksTooMuch => s"Got a Dog that barks too much"
case dog: Dog => s"Got a quiet Dog"
case x => s"Got an unexpected animal $x" }
The classify
method simply prints the appropriate message.
Let’s try it out.
scala> val frog1 = Frog(canSwim=true, 4, breathesAir=true) frog1: Frog = Frog(true,4,true)
scala> classify(frog1) res1: String = Got a Frog with 4 legs; canSwim=true and breathesAir=true
scala> val tadpole = Frog(canSwim=true, 0, breathesAir=false) tadpole: Frog = Frog(true,0,false)
scala> classify(tadpole) res2: String = Got a Frog with 0 legs; canSwim=true and breathesAir=false
scala> val bigDog = Dog(barksTooMuch=false) bigDog: Dog = Dog(false)
scala> classify(bigDog) res3: String = Got a Dog and barksTooMuch=false
scala> val yapper = Dog(barksTooMuch=true) yapper: Dog = Dog(true)
scala> classify(yapper) res4: String = Got a Dog and barksTooMuch=true
You can run this code by typing:
$ sbt "runMain PatMatch6" classify(frog1)=Got a Frog with 4 legs; canSwim=true and breathesAir=true classify(tadpole)=Got a tadpole without legs; breathesAir=false classify(bigDog)=Got a quiet Dog classify(yapper)=Got a Dog that barks too much
Order Matters
The order of case expressions matters for performance. For example, if one case appears 90% of the time it should precede others.
Exercise
Given an array of various types of values.
- Match against each type.
-
Each
match
case
should return aTuple2[Any, String]
, containing the matched value and aString
that states the matched value and the matching type. We discussed tuples in the Scala Tuples lecture earlier in this course. - Print out the string
Hints:
- You can declare an
Array
ofAny
like this:Scala codeArray("blah", 1, 1.0)
- You can loop through an array like this:
Scala code
Array("blah", 1, 1.0) foreach { valueToMatch => println(s"valueToMatch=$valueToMatch") }
- You can use the Java
getClass.getName
method to obtain the fully qualified name of an object’s class.
Solution
package solutions object PatMatch101a extends App { Array("blah", 1, 1.0) foreach { valueToMatch => val matchResult: (Any, String) = valueToMatch match { case string: String => (string, s"Got a String: $string") case int: Int => (int, s"Got an Int: $int") case double: Double => (double, s"Got a Double: $double") } println(s"matchResult=${matchResult._2} and is of type " + "${matchResult._1.getClass.getName}") } }
The code for this solution is provided in
courseNotes/
.
You can run it like this:
$ sbt "runMain solutions.PatMatch101a"
© Copyright 1994-2024 Michael Slinn. All rights reserved.
If you would like to request to use this copyright-protected work in any manner,
please send an email.
This website was made using Jekyll and Mike Slinn’s Jekyll Plugins.