Option-less pattern matching (original) (raw)
The implementation of pattern matching in Scala 3 was greatly simplified compared to Scala 2. From a user perspective, this means that Scala 3 generated patterns are a lot easier to debug, as variables all show up in debug modes and positions are correctly preserved.
Scala 3 supports a superset of Scala 2 extractors.
Boolean Match
U =:= Boolean
- Pattern-matching on exactly
0
patterns
For example:
object Even:
def unapply(s: String): Boolean = s.size % 2 == 0
"even" match
case s @ Even() => println(s"$s has an even number of characters")
case s => println(s"$s has an odd number of characters")
// even has an even number of characters
Product Match
U <: Product
N > 0
is the maximum number of consecutive (val
or parameterlessdef
)_1: P1
..._N: PN
members inU
- Pattern-matching on exactly
N
patterns with typesP1, P2, ..., PN
For example:
class FirstChars(s: String) extends Product:
def _1 = s.charAt(0)
def _2 = s.charAt(1)
// Not used by pattern matching: Product is only used as a marker trait.
def canEqual(that: Any): Boolean = ???
def productArity: Int = ???
def productElement(n: Int): Any = ???
object FirstChars:
def unapply(s: String): FirstChars = new FirstChars(s)
"Hi!" match
case FirstChars(char1, char2) =>
println(s"First: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi><mi>h</mi><mi>a</mi><mi>r</mi><mn>1</mn><mo separator="true">;</mo><mi>S</mi><mi>e</mi><mi>c</mi><mi>o</mi><mi>n</mi><mi>d</mi><mo>:</mo></mrow><annotation encoding="application/x-tex">char1; Second: </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">c</span><span class="mord mathnormal">ha</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord">1</span><span class="mpunct">;</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mord mathnormal">eco</span><span class="mord mathnormal">n</span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">:</span></span></span></span>char2")
// First: H; Second: i
Single Match
- Pattern-matching on
1
pattern with typeS
For example, where Nat <: R
, S = Int
:
class Nat(val x: Int):
def get: Int = x
def isEmpty = x < 0
object Nat:
def unapply(x: Int): Nat = new Nat(x)
5 match
case Nat(n) => println(s"$n is a natural number")
case _ => ()
// 5 is a natural number
Name-based Match
S
hasN > 1
members such that they are eachval
s or parameterlessdef
s, and named from_1
with typeP1
to_N
with typePN
S
doesn't haveN+1
members satisfying the previous point, i.e.N
is maximal- Pattern-matching on exactly
N
patterns with typesP1, P2, ..., PN
For example, where U = AlwaysEmpty.type <: R
, S = NameBased
:
object MyPatternMatcher:
def unapply(s: String) = AlwaysEmpty
object AlwaysEmpty:
def isEmpty = true
def get = NameBased
object NameBased:
def _1: Int = ???
def _2: String = ???
"" match
case MyPatternMatcher(_, _) => ???
case _ => ()
Sequence Match
V <: X
type X = {
def lengthCompare(len: Int): Int // or, `def length: Int`
def apply(i: Int): T1
def drop(n: Int): scala.Seq[T2]
def toSeq: scala.Seq[T3]
}
T2
andT3
conform toT1
- Pattern-matching on exactly
N
simple patterns with typesT1, T1, ..., T1
, whereN
is the runtime size of the sequence, or - Pattern-matching on
>= N
simple patterns and a vararg pattern (e.g.,xs: _*
) with typesT1, T1, ..., T1, Seq[T1]
, whereN
is the minimum size of the sequence.
For example, where V = S
, U = Option[S] <: R
, S = Seq[Char]
object CharList:
def unapplySeq(s: String): Option[Seq[Char]] = Some(s.toList)
"example" match
case CharList(c1, c2, c3, c4, _, _, _) =>
println(s"$c1,$c2,$c3,$c4")
case _ =>
println("Expected *exactly* 7 characters!")
// e,x,a,m
Product-Sequence Match
V <: Product
N > 0
is the maximum number of consecutive (val
or parameterlessdef
)_1: P1
..._N: PN
members inV
PN
conforms to the signatureX
defined in Seq Pattern- Pattern-matching on exactly
>= N
patterns, the firstN - 1
patterns have typesP1, P2, ... P(N-1)
, the type of the remaining patterns are determined as in Seq Pattern.
For example, where V = S
, U = Option[S] <: R
, S = (String, PN) <: Product
, PN = Seq[Int]
class Foo(val name: String, val children: Int*)
object Foo:
def unapplySeq(f: Foo): Option[(String, Seq[Int])] =
Some((f.name, f.children))
def foo(f: Foo) = f match
case Foo(name, x, y, ns*) => ">= two children."
case Foo(name, ns*) => "< two children."
There are plans for further simplification, in particular to factor out product match and name-based match into a single type of extractor.
Type testing
Abstract type testing with ClassTag
is replaced with TypeTest
or the alias Typeable
.
- pattern
_: X
for an abstract type requires aTypeTest
in scope - pattern
x @ X()
for an unapply that takes an abstract type requires aTypeTest
in scope