Skip to content

Commit 5a83b9b

Browse files
committed
Rewrote pattern matching tour
1 parent 6b9bd6e commit 5a83b9b

File tree

1 file changed

+109
-18
lines changed

1 file changed

+109
-18
lines changed

tutorials/tour/_posts/2017-02-13-pattern-matching.md

Lines changed: 109 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,38 +10,129 @@ num: 12
1010

1111
next-page: singleton-objects
1212
previous-page: case-classes
13+
prerequisite-knowledge: case-classes, string-interpolation, subtyping
1314
---
1415

15-
Scala has a built-in general pattern matching mechanism. It allows to match on any sort of data with a first-match policy.
16-
Here is a small example which shows how to match against an integer value:
16+
Pattern matching is a mechanism for checking a value for a pattern. It is a more powerful version of the `switch` statement in Java and it can likewise be used in place of a series of if/else statements.
1717

18+
## Syntax
19+
A match expression has a value, the `match` keyword, and at least one `case` clause.
1820
```tut
19-
object MatchTest1 extends App {
20-
def matchTest(x: Int): String = x match {
21-
case 1 => "one"
22-
case 2 => "two"
23-
case _ => "many"
24-
}
25-
println(matchTest(3))
21+
import scala.util.Random
22+
23+
val x: Int = new Random().nextInt(10)
24+
25+
x match {
26+
case 0 => "zero"
27+
case 1 => "one"
28+
case 2 => "two"
29+
case _ => "many"
2630
}
2731
```
32+
The `val x` above is a random integer between 0 and 10. `x` becomes the left operand of the `match` operator and on the right is an expression with four cases. The last case `_` is a "catch all" case for any number greater than 2. Cases are also called _alternatives_.
2833

29-
The block with the `case` statements defines a function which maps integers to strings. The `match` keyword provides a convenient way of applying a function (like the pattern matching function above) to an object.
34+
Match expressions have a value.
35+
```tut
36+
def matchTest(x: Int): String = x match {
37+
case 1 => "one"
38+
case 2 => "two"
39+
case _ => "many"
40+
}
41+
matchTest(3) // many
42+
matchTest(1) // one
43+
```
44+
This match expression has a type String because all of the cases return String. Therefore, the function `matchTest` returns a String.
45+
46+
## Matching on case classes
3047

31-
Here is a second example which matches a value against patterns of different types:
48+
Case classes are especially useful for pattern matching.
3249

3350
```tut
34-
object MatchTest2 extends App {
35-
def matchTest(x: Any): Any = x match {
36-
case 1 => "one"
37-
case "two" => 2
38-
case y: Int => "scala.Int"
51+
abstract class Notification
52+
53+
case class Email(sender: String, title: String, body: String) extends Notification
54+
55+
case class SMS(caller: String, message: String) extends Notification
56+
57+
case class VoiceRecording(contactName: String, link: String) extends Notification
58+
59+
60+
```
61+
`Notification` is an abstract super class which has three concrete Notification types implemented with case classes `Email`, `SMS`, and `VoiceRecording`. Now we can do pattern matching on these case classes:
62+
63+
```
64+
def showNotification(notification: Notification): String = {
65+
notification match {
66+
case Email(email, title, _) =>
67+
s"You got an email from $email with title: $title"
68+
case SMS(number, message) =>
69+
s"You got an SMS from $number! Message: $message"
70+
case VoiceRecording(name, link) =>
71+
s"you received a Voice Recording from $name! Click the link to hear it: $link"
72+
}
73+
}
74+
val someSms = SMS("12345", "Are you there?")
75+
val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123")
76+
77+
println(showNotification(someSms)) // prints You got an SMS from 12345! Message: Are you there?
78+
79+
println(showNotification(someVoiceRecording)) // you received a Voice Recording from Tom! Click the link to hear it: voicerecording.org/id/123
80+
```
81+
The function `showNotification` takes as a parameter the abstract type `Notification` and matches on the type of `Notification` (i.e. it figures out whether it's an `Email`, `SMS`, or `VoiceRecording`). In the `case Email(email, title, _)` the fields `email` and `title` are used in the return value but the `body` field is ignored with `_`.
82+
83+
## Pattern guards
84+
```
85+
def showNotificationSpecial(notification: Notification, specialEmail: String, specialNumber: String): String = {
86+
notification match {
87+
case Email(email, _, _) if email == specialEmail =>
88+
"You got an email from special someone!"
89+
case SMS(number, _) if number == specialNumber =>
90+
"You got an SMS from special someone!"
91+
case other =>
92+
showNotification(other) // nothing special, delegate to our original showNotification function
3993
}
40-
println(matchTest("two"))
94+
}
95+
96+
val SPECIAL_NUMBER = "55555"
97+
val SPECIAL_EMAIL = "[email protected]"
98+
val someSms = SMS("12345", "Are you there?")
99+
val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123")
100+
val specialEmail = Email("[email protected]", "Drinks tonight?", "I'm free after 5!")
101+
val specialSms = SMS("55555", "I'm here! Where are you?")
102+
103+
println(showNotificationSpecial(someSms, SPECIAL_EMAIL, SPECIAL_NUMBER))
104+
println(showNotificationSpecial(someVoiceRecording, SPECIAL_EMAIL, SPECIAL_NUMBER))
105+
println(showNotificationSpecial(specialEmail, SPECIAL_EMAIL, SPECIAL_NUMBER))
106+
println(showNotificationSpecial(specialSms, SPECIAL_EMAIL, SPECIAL_NUMBER))
107+
```
108+
109+
## Matching on type
110+
It is also possible to match on type.
111+
```tut
112+
def printType(x: Any): Unit = x match {
113+
case s: String => println(s"$s is a String")
114+
case i: Int => println(s"$i is an Int")
115+
case u:_ => println(s"$u is something else")
116+
}
117+
```
118+
In this case, we're actually matching on the _type_ of `s`, `i`, and `u`. It is a convention to use the first letter of the type as the case identifier.
119+
120+
## Sealed classes
121+
Traits and classes can be marked `sealed` which means all subtypes must be declared in the same file. The assures that all subtypes are known.
122+
123+
```tut
124+
sealed abstract class Furniture
125+
case class Couch() extends Furniture
126+
case class Chair() extends Furniture
127+
128+
def findPlaceToSit(piece: Furniture): String = piece match {
129+
case a: Couch => "Lie on the couch"
130+
case b: Chair => "Sit on the chair"
41131
}
42132
```
133+
This is useful for pattern matching because we don't need a "catch all" case.
43134

44-
The first `case` matches if `x` refers to the integer value `1`. The second `case` matches if `x` is equal to the string `"two"`. The third case consists of a typed pattern; it matches against any integer and binds the selector value `x` to the variable `y` of type integer.
135+
## Notes
45136

46137
Scala's pattern matching statement is most useful for matching on algebraic types expressed via [case classes](case-classes.html).
47138
Scala also allows the definition of patterns independently of case classes, using `unapply` methods in [extractor objects](extractor-objects.html).

0 commit comments

Comments
 (0)