Skip to content

Commit e54bc10

Browse files
committed
Add Scala 3 tabs in _tour/pattern-matching
1 parent 9f831d6 commit e54bc10

File tree

1 file changed

+125
-5
lines changed

1 file changed

+125
-5
lines changed

_tour/pattern-matching.md

Lines changed: 125 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Pattern matching is a mechanism for checking a value against a pattern. A succes
1515

1616
## Syntax
1717
A match expression has a value, the `match` keyword, and at least one `case` clause.
18+
{% tabs pattern-matching-1 class=tabs-scala-version %}
19+
{% tab 'Scala 2' for=pattern-matching-1 %}
1820
```scala mdoc
1921
import scala.util.Random
2022

@@ -27,9 +29,26 @@ x match {
2729
case _ => "other"
2830
}
2931
```
32+
{% endtab %}
33+
{% tab 'Scala 3' for=pattern-matching-1 %}
34+
```scala
35+
import scala.util.Random
36+
37+
val x: Int = Random.nextInt(10)
38+
39+
x match
40+
case 0 => "zero"
41+
case 1 => "one"
42+
case 2 => "two"
43+
case _ => "other"
44+
```
45+
{% endtab %}
46+
{% endtabs %}
3047
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 other possible `Int` values. Cases are also called _alternatives_.
3148

3249
Match expressions have a value.
50+
{% tabs pattern-matching-2 class=tabs-scala-version %}
51+
{% tab 'Scala 2' for=pattern-matching-2 %}
3352
```scala mdoc
3453
def matchTest(x: Int): String = x match {
3554
case 1 => "one"
@@ -39,6 +58,20 @@ def matchTest(x: Int): String = x match {
3958
matchTest(3) // returns other
4059
matchTest(1) // returns one
4160
```
61+
{% endtab %}
62+
63+
{% tab 'Scala 3' for=pattern-matching-2 %}
64+
```scala
65+
def matchTest(x: Int): String = x match
66+
case 1 => "one"
67+
case 2 => "two"
68+
case _ => "other"
69+
70+
matchTest(3) // returns other
71+
matchTest(1) // returns one
72+
```
73+
{% endtab %}
74+
{% endtabs %}
4275
This match expression has a type String because all of the cases return String. Therefore, the function `matchTest` returns a String.
4376

4477
## Matching on case classes
@@ -53,12 +86,12 @@ case class Email(sender: String, title: String, body: String) extends Notificati
5386
case class SMS(caller: String, message: String) extends Notification
5487

5588
case class VoiceRecording(contactName: String, link: String) extends Notification
56-
57-
5889
```
5990
`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:
6091

61-
```
92+
{% tabs pattern-matching-4 class=tabs-scala-version %}
93+
{% tab 'Scala 2' for=pattern-matching-4 %}
94+
```scala
6295
def showNotification(notification: Notification): String = {
6396
notification match {
6497
case Email(sender, title, _) =>
@@ -76,12 +109,36 @@ println(showNotification(someSms)) // prints You got an SMS from 12345! Message
76109

77110
println(showNotification(someVoiceRecording)) // prints You received a Voice Recording from Tom! Click the link to hear it: voicerecording.org/id/123
78111
```
112+
{% endtab %}
113+
{% tab 'Scala 3' for=pattern-matching-4 %}
114+
```scala
115+
def showNotification(notification: Notification): String =
116+
notification match
117+
case Email(sender, title, _) =>
118+
s"You got an email from $sender with title: $title"
119+
case SMS(number, message) =>
120+
s"You got an SMS from $number! Message: $message"
121+
case VoiceRecording(name, link) =>
122+
s"You received a Voice Recording from $name! Click the link to hear it: $link"
123+
124+
val someSms = SMS("12345", "Are you there?")
125+
val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123")
126+
127+
println(showNotification(someSms)) // prints You got an SMS from 12345! Message: Are you there?
128+
129+
println(showNotification(someVoiceRecording)) // prints You received a Voice Recording from Tom! Click the link to hear it: voicerecording.org/id/123
130+
```
131+
{% endtab %}
132+
{% endtabs %}
133+
79134
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(sender, title, _)` the fields `sender` and `title` are used in the return value but the `body` field is ignored with `_`.
80135

81136
## Pattern guards
82137
Pattern guards are simply boolean expressions which are used to make cases more specific. Just add `if <boolean expression>` after the pattern.
83-
```
84138

139+
{% tabs pattern-matching-5 class=tabs-scala-version %}
140+
{% tab 'Scala 2' for=pattern-matching-5 %}
141+
```scala
85142
def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String = {
86143
notification match {
87144
case Email(sender, _, _) if importantPeopleInfo.contains(sender) =>
@@ -106,11 +163,40 @@ println(showImportantNotification(importantEmail, importantPeopleInfo)) // print
106163

107164
println(showImportantNotification(importantSms, importantPeopleInfo)) // prints You got an SMS from special someone!
108165
```
166+
{% endtab %}
167+
{% tab 'Scala 3' for=pattern-matching-5 %}
168+
```scala
169+
def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String =
170+
notification match
171+
case Email(sender, _, _) if importantPeopleInfo.contains(sender) =>
172+
"You got an email from special someone!"
173+
case SMS(number, _) if importantPeopleInfo.contains(number) =>
174+
"You got an SMS from special someone!"
175+
case other =>
176+
showNotification(other) // nothing special, delegate to our original showNotification function
177+
178+
val importantPeopleInfo = Seq("867-5309", "[email protected]")
179+
180+
val someSms = SMS("123-4567", "Are you there?")
181+
val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123")
182+
val importantEmail = Email("[email protected]", "Drinks tonight?", "I'm free after 5!")
183+
val importantSms = SMS("867-5309", "I'm here! Where are you?")
184+
185+
println(showImportantNotification(someSms, importantPeopleInfo)) // prints You got an SMS from 123-4567! Message: Are you there?
186+
println(showImportantNotification(someVoiceRecording, importantPeopleInfo)) // prints You received a Voice Recording from Tom! Click the link to hear it: voicerecording.org/id/123
187+
println(showImportantNotification(importantEmail, importantPeopleInfo)) // prints You got an email from special someone!
188+
189+
println(showImportantNotification(importantSms, importantPeopleInfo)) // prints You got an SMS from special someone!
190+
```
191+
{% endtab %}
192+
{% endtabs %}
109193

110194
In the `case Email(sender, _, _) if importantPeopleInfo.contains(sender)`, the pattern is matched only if the `sender` is in the list of important people.
111195

112196
## Matching on type only
113197
You can match on the type like so:
198+
{% tabs pattern-matching-6 class=tabs-scala-version %}
199+
{% tab 'Scala 2' for=pattern-matching-6 %}
114200
```scala mdoc
115201
abstract class Device
116202
case class Phone(model: String) extends Device {
@@ -120,16 +206,36 @@ case class Computer(model: String) extends Device {
120206
def screenSaverOn = "Turning screen saver on..."
121207
}
122208

123-
def goIdle(device: Device) = device match {
209+
def goIdle(device: Device): String = device match {
124210
case p: Phone => p.screenOff
125211
case c: Computer => c.screenSaverOn
126212
}
127213
```
214+
{% endtab %}
215+
{% tab 'Scala 3' for=pattern-matching-6 %}
216+
```scala
217+
abstract class Device
218+
case class Phone(model: String) extends Device:
219+
def screenOff = "Turning screen off"
220+
221+
case class Computer(model: String) extends Device:
222+
def screenSaverOn = "Turning screen saver on..."
223+
224+
225+
def goIdle(device: Device): String = device match
226+
case p: Phone => p.screenOff
227+
case c: Computer => c.screenSaverOn
228+
```
229+
{% endtab %}
230+
{% endtabs %}
231+
128232
`def goIdle` has a different behavior depending on the type of `Device`. This is useful when the case needs to call a method on the pattern. It is a convention to use the first letter of the type as the case identifier (`p` and `c` in this case).
129233

130234
## Sealed classes
131235
Traits and classes can be marked `sealed` which means all subtypes must be declared in the same file. This assures that all subtypes are known.
132236

237+
{% tabs pattern-matching-7 class=tabs-scala-version %}
238+
{% tab 'Scala 2' for=pattern-matching-7 %}
133239
```scala mdoc
134240
sealed abstract class Furniture
135241
case class Couch() extends Furniture
@@ -140,6 +246,20 @@ def findPlaceToSit(piece: Furniture): String = piece match {
140246
case b: Chair => "Sit on the chair"
141247
}
142248
```
249+
{% endtab %}
250+
{% tab 'Scala 3' for=pattern-matching-7 %}
251+
```scala
252+
sealed abstract class Furniture
253+
case class Couch() extends Furniture
254+
case class Chair() extends Furniture
255+
256+
def findPlaceToSit(piece: Furniture): String = piece match
257+
case a: Couch => "Lie on the couch"
258+
case b: Chair => "Sit on the chair"
259+
```
260+
{% endtab %}
261+
{% endtabs %}
262+
143263
This is useful for pattern matching because we don't need a "catch all" case.
144264

145265
## Notes

0 commit comments

Comments
 (0)