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 <: Rand- 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 0patterns
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 > 0is the maximum number of consecutive (- valor parameterless- def)- _1: P1...- _N: PNmembers in- U
- Pattern-matching on exactly Npatterns 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: $char1; Second: $char2")
// First: H; Second: i
Single Match
- Pattern-matching on 1pattern 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
- Shas- N > 1members such that they are each- vals or parameterless- defs, and named from- _1with type- P1to- _Nwith type- PN
- Sdoesn't have- N+1members satisfying the previous point, i.e.- Nis maximal
- Pattern-matching on exactly Npatterns 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 _ => ()
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
- Vis valid if- Vconforms to one of the following matches:
- Otherwise Uhas to conform to the typeR:
type R = {
  def isEmpty: Boolean
  def get: S
}
- Set V := S, and reattempt 2., if it failsUis 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 <: Rand- 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]
}
- T2and- T3conform to- T1
- Pattern-matching on exactly Nsimple patterns with typesT1, T1, ..., T1, whereNis the runtime size of the sequence, or
- Pattern-matching on >= Nsimple patterns and a vararg pattern (e.g.,xs: _*) with typesT1, T1, ..., T1, Seq[T1], whereNis 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 > 0is the maximum number of consecutive (- valor parameterless- def)- _1: P1...- _N: PNmembers in- V
- PNconforms to the signature- Xdefined in Seq Pattern
- Pattern-matching on exactly >= Npatterns, the firstN - 1patterns 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 _: Xfor an abstract type requires aTypeTestin scope
- pattern x @ X()for an unapply that takes an abstract type requires aTypeTestin scope