# Option-less pattern matching

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.

Dotty supports a superset of scalac's extractors.

## Extractors

Extractors are objects that expose a method `unapply` or `unapplySeq`:

``````def unapply[A](x: T)(implicit x: B): U
def unapplySeq[A](x: T)(implicit x: B): U
``````

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

### Fixed-Arity Extractors

Fixed-arity extractors expose the following signature:

``````def unapply[A](x: T)(implicit x: B): U
``````

The type `U` conforms to one of the following matches:

• Boolean match
• Product match

Or `U` conforms to the type `R`:

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

and `S` conforms to one of the following matches:

• single match
• name-based match

The former form of `unapply` has higher precedence, and single match has higher precedence over name-based match.

Variadic extractors expose the following signature:

``````def unapplySeq[A](x: T)(implicit x: B): U
``````

The type `U` conforms to one of the following matches:

• sequence match
• product-sequence match

Or `U` conforms to the type `R`:

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

and `S` conforms to one of the two matches above.

The former form of `unapplySeq` has higher priority, and sequence match has higher precedence over product-sequence match.

## 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 (parameterless `def` or `val`) `_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

• If there is exactly `1` pattern, pattern-matching on `1` pattern with type `U`
``````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

• `N > 1` is the maximum number of consecutive (parameterless `def` or `val`) `_1: P1 ... _N: PN` members in `U`
• Pattern-matching on exactly `N` patterns with types `P1, P2, ..., PN`
``````object ProdEmpty {
def _1: Int = ???
def _2: String = ???
def isEmpty = true
def unapply(s: String): this.type = this
def get = this
}

"" match {
case ProdEmpty(_, _) => ???
case _ => ()
}
``````

## Sequence Match

• `U <: X`, `T2` and `T3` conform to `T1`
``````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]
}
``````
• 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.
``````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

• `U <: Product`
• `N > 0` is the maximum number of consecutive (parameterless `def` or `val`) `_1: P1` ... `_N: PN` members in `U`
• `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.
``````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, ns : _*) =>
case Foo(name, x, y, ns : _*) =>
}
``````

There are plans for further simplification, in particular to factor out product match and name-based match into a single type of extractor.