scalaintermediate
Custom Extractors with unapply
Create custom pattern matching extractors: unapply, unapplySeq, and boolean extractors.
scalaPress ⌘/Ctrl + Shift + C to copy
// Simple extractor
object Email:
def unapply(s: String): Option[(String, String)] =
s.split("@") match
case Array(local, domain) if local.nonEmpty && domain.contains(".") =>
Some((local, domain))
case _ => None
// Boolean extractor (no value, just test)
object Even:
def unapply(n: Int): Boolean = n % 2 == 0
object Positive:
def unapply(n: Int): Boolean = n > 0
// Extracting multiple values
object RGB:
def unapply(hex: String): Option[(Int, Int, Int)] =
val clean = hex.stripPrefix("#")
if clean.length == 6 then
try
val r = Integer.parseInt(clean.substring(0, 2), 16)
val g = Integer.parseInt(clean.substring(2, 4), 16)
val b = Integer.parseInt(clean.substring(4, 6), 16)
Some((r, g, b))
catch case _: NumberFormatException => None
else None
// Sequence extractor
object Words:
def unapplySeq(s: String): Option[Seq[String]] =
val words = s.trim.split("\\s+").toSeq
if words.nonEmpty && words.head.nonEmpty then Some(words)
else None
// Name extractor
object Name:
def unapply(fullName: String): Option[(String, Option[String], String)] =
fullName.trim.split("\\s+").toList match
case first :: last :: Nil => Some((first, None, last))
case first :: middle :: last :: Nil => Some((first, Some(middle), last))
case _ => None
// URL extractor
object URL:
def unapply(url: String): Option[(String, String, String)] =
val pattern = """(https?)://([^/]+)(/.*)?""".r
url match
case pattern(scheme, host, path) =>
Some((scheme, host, Option(path).getOrElse("/")))
case _ => None
// Range extractor
object Between:
def unapply(n: Int): Option[(Int, Int, Int)] =
Some((n, n, n)) // just passes through for guard use
// Infix extractor pattern
case class ~[+A, +B](a: A, b: B)
@main def run(): Unit =
// Email
"alice@example.com" match
case Email(local, domain) => println(s"Email: $local @ $domain")
case _ => println("Not an email")
"not-email" match
case Email(_, _) => println("Email")
case s => println(s"Not email: $s")
// Boolean extractors
42 match
case Even() => println("42 is even")
case _ => println("42 is odd")
// Combined
val n = 7
n match
case Even() & Positive() => println(s"$n: even and positive")
case Positive() => println(s"$n: positive but odd")
case _ => println(s"$n: other")
// RGB
"#FF8000" match
case RGB(r, g, b) => println(s"Color: R=$r, G=$g, B=$b")
case _ => println("Invalid")
// Words
"hello beautiful world" match
case Words(first, _*) => println(s"First word: $first")
case _ => println("No words")
"the quick brown fox" match
case Words(a, b, c, d) => println(s"Four words: $a $b $c $d")
case _ => ()
// Name
List("John Doe", "John M Doe", "Cher").foreach {
case Name(first, Some(mid), last) =>
println(s" $first $mid. $last")
case Name(first, None, last) =>
println(s" $first $last")
case name =>
println(s" Cannot parse: $name")
}
// URL
"https://example.com/api/v1" match
case URL(scheme, host, path) =>
println(s"URL: $scheme://$host$path")
case _ => println("Invalid URL")Use Cases
- Domain-specific pattern matching
- Parsing structured strings
- Custom destructuring in match
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
scalaadvanced
Advanced Pattern Matching Techniques
Master advanced patterns: custom extractors, unapply, variable binding, and deep matching.
Best for: Custom domain extractors
#scala#pattern-matching
scalaadvanced
DSL Builder Pattern
Create type-safe domain-specific languages with Scala's builder pattern using apply and infix notation.
Best for: Type-safe query builders
#scala#dsl
scalabeginner
Scala Hello World Application
Create a basic Scala application with main method, string interpolation, and val/var basics.
Best for: Getting started with Scala
#scala#basics
scalabeginner
Pattern Matching Fundamentals
Use Scala pattern matching with guards, type patterns, tuple patterns, and nested extractors.
Best for: Control flow with pattern matching
#scala#pattern-matching