# Option-less pattern matching

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.

## Extractors

Extractors are objects that expose a method `unapply`

or `unapplySeq`

:

```
def unapply(x: T): U
def unapplySeq(x: T): U
```

Where `T`

is an arbitrary type, if it is a subtype of the scrutinee's type `Scrut`

, a type test is performed before calling the method. `U`

follows rules described in Fixed Arity Extractors and Variadic Extractors.

**Note:** `U`

can be the type of the extractor object.

`unapply`

and `unapplySeq`

can actually have a more general signature, allowing for a leading type clause, as well as arbitrarily many using clauses, both before and after the regular term clause, and at most one implicit clause at the end, for example:

```
def unapply[A, B](using C)(using D)(x: T)(using E)(using F)(implicit y: G): U = ???
```

Extractors that expose the method `unapply`

are called fixed-arity extractors, which work with patterns of fixed arity. Extractors that expose the method `unapplySeq`

are called variadic extractors, which enables variadic patterns.

## Fixed-Arity Extractors

Fixed-arity extractors expose the following signature (with potential type, using and implicit clauses):

```
def unapply(x: T): U
```

The type `U`

conforms to one of the following matches:

Or `U`

conforms to the type `R`

:

```
type R = {
def isEmpty: Boolean
def get: S
}
```

and `S`

conforms to one of the following matches:

The former form of `unapply`

has higher precedence, and *single match* has higher precedence over *name-based match*.

**Note:** the `S`

in `R`

can be `U`

.

A usage of a fixed-arity extractor is irrefutable if one of the following condition holds:

`U = true`

- the extractor is used as a product match
`U <: R`

and`U <: { def isEmpty: false }`

`U = Some[T]`

**Note:** The last rule is necessary because, for compatibility reasons, `isEmpty`

on `Some`

has return type `Boolean`

rather than `false`

, even though it always returns `false`

.

### 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 parameterless`def`

)`_1: P1`

...`_N: PN`

members in`U`

- Pattern-matching on exactly
`N`

patterns with types`P1, 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: $char1; Second: $char2")
// First: H; Second: i
```

### Single Match

- Pattern-matching on
`1`

pattern with type`S`

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`

has`N > 1`

members such that they are each`val`

s or parameterless`def`

s, and named from`_1`

with type`P1`

to`_N`

with type`PN`

`S`

doesn't have`N+1`

members satisfying the previous point, i.e.`N`

is maximal- Pattern-matching on exactly
`N`

patterns with types`P1, 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 _ => ()
```

## Variadic Extractors

Variadic extractors expose the following signature (with potential type, using and implicit clauses):

```
def unapplySeq(x: T): U
```

Where `U`

has to fullfill the following:

- Set
`V := U`

`V`

is valid if`V`

conforms to one of the following matches:

- Otherwise
`U`

has to conform to the type`R`

:

```
type R = {
def isEmpty: Boolean
def get: S
}
```

- Set
`V := S`

, and reattempt 2., if it fails`U`

is not valid.

The `V := U`

form of `unapplySeq`

has higher priority, and *sequence match* has higher precedence over *product-sequence match*.

**Note:** This means `isEmpty`

is disregarded if the `V := U`

form is valid

A usage of a variadic extractor is irrefutable if one of the following conditions holds:

- the extractor is used directly as a sequence match or product-sequence match
`U <: R`

and`U <: { def isEmpty: false }`

`U = Some[T]`

**Note:** The last rule is necessary because, for compatibility reasons, `isEmpty`

on `Some`

has return type `Boolean`

rather than `false`

, even though it always returns `false`

.

**Note:** Be careful, by the first condition and the note above, it is possible to define an irrefutable extractor with a `def isEmpty: true`

. This is strongly discouraged and, if found in the wild, is almost certainly a bug.

### 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`

and`T3`

conform to`T1`

- Pattern-matching on
*exactly*`N`

simple patterns with types`T1, T1, ..., T1`

, where`N`

is the runtime size of the sequence, or - Pattern-matching on
`>= N`

simple patterns and*a vararg pattern*(e.g.,`xs: _*`

) with types`T1, T1, ..., T1, Seq[T1]`

, where`N`

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 parameterless`def`

)`_1: P1`

...`_N: PN`

members in`V`

`PN`

conforms to the signature`X`

defined in Seq Pattern- Pattern-matching on exactly
`>= N`

patterns, the first`N - 1`

patterns have types`P1, 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 a`TypeTest`

in scope - pattern
`x @ X()`

for an unapply that takes an abstract type requires a`TypeTest`

in scope