Pattern-Matching Complexity¶
Pattern matching is expressive, but it can also hide a lot of branching and nesting. This metric family makes that branching visible and quantifiable.
What it Measures¶
- Matches - number of
matchexpressions. - Cases - total number of case arms across all matches.
- Average cases per match - cases / matches.
- Guards - number of guarded cases (
case ... if cond =>). - Wildcards - number of wildcard/default cases (
case _ =>). - Maximum Match Nesting - deepest nesting level of match inside match.
- Nested Matches - how many match nodes appear inside another match.
How it's Measured¶
- In function slice:
- Count
match. - For each, count
caselines (includingcase _). - Count guards (
case ... if ...).
- Count
- (Optional) Exhaustivity requires sealed-hierarchy knowledge beyond basic SemanticDB scans.
Why it helps¶
Points to complex decision logic candidates for splitting, table-driven logic, or polymorphism.
Pattern Matching Usage: Lean on the Type System¶
Pattern matching shines when your domain is a finite set of cases. Sealed ADTs let the compiler prove you’ve handled them all. Guards express small refinements. As cases grow complex, extract helpers or move behavior into the types themselves.
1. Make illegal states unrepresentable¶
Booleans and strings invite illegal combinations (kind="card", valid=false but still processed). ADTs move invalid
states out of the realm of possibility. Pattern matches over Payment become self‑documenting and safe.
Usage:
def fee(p: Payment): BigDecimal = p match {
case Card(a, _) => a * 0.02
case Wire(a, _) => a * 0.01
case Cash(a) => 0
}
2. Pattern guards vs. nested blocks¶
Guards let you keep a small condition close to the case. As soon as a branch needs more than a couple of steps, a helper keeps the match readable and testable.
Note
Choose guards for tiny predicates; prefer helpers as soon as work grows.
3. Custom extractors (unapply) for clarity¶
Extractors let you speak the domain in your matches. Instead of opaque regex checks, your cases say exactly what you’re pulling out. Tests become simpler and intention revealing.
4. When to prefer polymorphism¶
If you find a central match that keeps growing and is used all over the codebase, consider moving the behavior onto
the types (OO polymorphism) so call sites stop branching. Keep matches for one‑off decisions, constructors, and simple
routing.