Skip to content

Commit 70d896c

Browse files
Add pattern matching documentation
1 parent 68b947d commit 70d896c

File tree

4 files changed

+184
-0
lines changed

4 files changed

+184
-0
lines changed
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
---
2+
layout: doc-page
3+
title: "Option-less pattern matching"
4+
---
5+
6+
Dotty implementation of pattern matching was greatly simplified compared to scalac. From a user perspective, this means that Dotty generated patterns are a *lot* easier to debug, as variables all show up in debug modes and positions are correctly preserved.
7+
8+
Dotty supports a superset of scalac's [extractors](https://www.scala-lang.org/files/archive/spec/2.13/08-pattern-matching.html#extractor-patterns).
9+
10+
## Boolean Pattern
11+
12+
- Extractor defines `def unapply(x: T): Boolean`
13+
- Pattern-matching on exactly `0` patterns
14+
15+
For example:
16+
17+
<!-- To be kept in sync with tests/new/patmat-spec.scala -->
18+
19+
```scala
20+
object Even {
21+
def unapply(s: String): Boolean = s.size % 2 == 0
22+
}
23+
24+
"even" match {
25+
case s @ Even() => println(s"$s has an even number of characters")
26+
case s => println(s"$s has an odd number of characters")
27+
}
28+
// even has an even number of characters
29+
```
30+
31+
32+
## Product Pattern
33+
34+
- Extractor defines `def unapply(x: T): U`
35+
- `U <: Product`
36+
- `N > 0` is the maximum number of consecutive (parameterless `def` or `val`) `_1: P1` ... `_N: PN` members in `U`
37+
- Pattern-matching on exactly `N` patterns with types `P1, P2, ..., PN`
38+
39+
For example:
40+
41+
<!-- To be kept in sync with tests/new/patmat-spec.scala -->
42+
43+
```scala
44+
class FirstChars(s: String) extends Product {
45+
def _1 = s.charAt(0)
46+
def _2 = s.charAt(1)
47+
48+
// Not used by pattern matching: Product is only used as a marker trait.
49+
def canEqual(that: Any): Boolean = ???
50+
def productArity: Int = ???
51+
def productElement(n: Int): Any = ???
52+
}
53+
54+
object FirstChars {
55+
def unapply(s: String): FirstChars = new FirstChars(s)
56+
}
57+
58+
"Hi!" match {
59+
case FirstChars(char1, char2) =>
60+
println(s"First: $char1; Second: $char2")
61+
}
62+
// First: H; Second: i
63+
```
64+
65+
66+
## Seq Pattern
67+
68+
- Extractor defines `def unapplySeq(x: T): U`
69+
- `U` has (parameterless `def` or `val`) members `isEmpty: Boolean` and `get: S`
70+
- `S <: Seq[V]`
71+
- Pattern-matching on `N` pattern with types `V, V, ..., V`, where `N` is the runtime size of the `Seq`.
72+
73+
<!-- To be kept in sync with tests/new/patmat-spec.scala -->
74+
75+
```scala
76+
object CharList {
77+
def unapplySeq(s: String): Option[Seq[Char]] = Some(s.toList)
78+
}
79+
80+
"example" match {
81+
case CharList(c1, c2, c3, c4, _, _, _) =>
82+
println(s"$c1,$c2,$c3,$c4")
83+
case _ =>
84+
println("Expected *exactly* 7 characters!")
85+
}
86+
// e,x,a,m
87+
```
88+
89+
90+
## Name Based Pattern
91+
92+
- Extractor defines `def unapply(x: T): U`
93+
- `U` has (parameterless `def` or `val`) members `isEmpty: Boolean` and `get: S`
94+
- If there is exactly `1` pattern, pattern-matching on `1` pattern with type `S`
95+
- Otherwise `N > 0` is the maximum number of consecutive (parameterless `def` or `val`) `_1: P1` ... `_N: PN` members in `U`
96+
- Pattern-matching on exactly `N` patterns with types `P1, P2, ..., PN`
97+
98+
<!-- To be kept in sync with tests/new/patmat-spec.scala -->
99+
100+
```scala
101+
class Nat(val x: Int) {
102+
def get: Int = x
103+
def isEmpty = x < 0
104+
}
105+
106+
object Nat {
107+
def unapply(x: Int): Nat = new Nat(x)
108+
}
109+
110+
5 match {
111+
case Nat(n) => println(s"$n is a natural number")
112+
case _ => ()
113+
}
114+
// 5 is a natural number
115+
```
116+
117+
In case of ambiguities, *Product Pattern* is preferred over *Name Based Pattern*. This document reflects the state of pattern matching as currently implemented in Dotty. They are plans for further simplification, in particular to factor out *Product Pattern* and *Name Based Pattern* into a single type of extractor.

docs/sidebar.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ sidebar:
5151
url: docs/reference/changed/implicit-conversions.html
5252
- title: Vararg Patterns
5353
url: docs/reference/changed/vararg-patterns.html
54+
- title: Pettern matching
55+
url: docs/reference/changed/pattern-matching.html
5456
- title: Dropped Features
5557
subsection:
5658
- title: DelayedInit

tests/new/patmat-spec.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
even has an even number of characters
2+
First: H; Second: i
3+
e,x,a,m
4+
5 is a natural number

tests/run/patmat-spec.scala

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// To be kept in sync with docs/docs/reference/pattern-matching.md
2+
object Test {
3+
def main(args: Array[String]): Unit = {
4+
object Even {
5+
def unapply(s: String): Boolean = s.size % 2 == 0
6+
}
7+
8+
"even" match {
9+
case s @ Even() => println(s"$s has an even number of characters")
10+
case s => println(s"$s has an odd number of characters")
11+
}
12+
// even has an even number of characters
13+
14+
class FirstChars(s: String) extends Product {
15+
def _1 = s.charAt(0)
16+
def _2 = s.charAt(1)
17+
18+
// Not used by pattern matching: Product is only used as a marker trait.
19+
def canEqual(that: Any): Boolean = ???
20+
def productArity: Int = ???
21+
def productElement(n: Int): Any = ???
22+
}
23+
24+
object FirstChars {
25+
def unapply(s: String): FirstChars = new FirstChars(s)
26+
}
27+
28+
"Hi!" match {
29+
case FirstChars(char1, char2) =>
30+
println(s"First: $char1; Second: $char2")
31+
}
32+
// First: H; Second: i
33+
34+
object CharList {
35+
def unapplySeq(s: String): Option[Seq[Char]] = Some(s.toList)
36+
}
37+
38+
"example" match {
39+
case CharList(c1, c2, c3, c4, _, _, _) =>
40+
println(s"$c1,$c2,$c3,$c4")
41+
case _ =>
42+
println("Expected *exactly* 7 characters!")
43+
}
44+
// e,x,a,m
45+
46+
class Nat(val x: Int) {
47+
def get: Int = x
48+
def isEmpty = x < 0
49+
}
50+
51+
object Nat {
52+
def unapply(x: Int): Nat = new Nat(x)
53+
}
54+
55+
5 match {
56+
case Nat(n) => println(s"$n is a natural number")
57+
case _ => ()
58+
}
59+
// 5 is a natural number
60+
}
61+
}

0 commit comments

Comments
 (0)