Last Updated: February 25, 2016
· eallik

Scala extractors simplified

Extractors seem to be nothing more than lifted and wrapped PartialFunctions:

type ?=>[A, B] = PartialFunction[A, B]

trait Extractor[A, B] { def unapply(x: A): Option[B] }
def Extractor[A, B](fn: A ?=> B) = new Extractor[A, B] {
  def unapply(x: A): Option[B] = fn.lift(x)

Let's use that framework on some data:

val people = List(
  Person("Bradley", "Smith"),
  Person("Paul", "Freeman", Some(Position("Programmer"))),
  Person("Mary", "Martyr", Some(Position("Manager", Some(Company("IBM", Some(1881)))))))

Now we have the option to define an extractor to get the company a person works at, if any, or instead start out with a more bare bones function; let's go bare bones first:

val getCompany: Person ?=> Company = {
  case Person(_, _, Some(Position(_, Some(c)))) => c

We'd have to use longer syntax here if instead of getCompany we'd defined a full blown extractor straight away:

val companies = people.collect(getCompany).distinct

Now we can convert getCompany into an extractor called EmployedAt for added pattern matching convenience:

val EmployedAt = Extractor(getCompany)

Extractors compose better in more complex pattern matching queries:

def getEmployees(company: Company) =
  people.collect { case p @ EmployedAt(`company`) => p }