Scala Language Extractors Transformative extractors


Example

Extractor behavior can be used to derive arbitrary values from their input. This can be useful in scenarios where you want to be able to act on the results of a transformation in the event that the transformation is successful.

Consider as an example the various user name formats usable in a Windows environment:

object UserPrincipalName {
  def unapply(str: String): Option[(String, String)] = str.split('@') match {
    case Array(u, d) if u.length > 0 && d.length > 0 => Some((u, d))
    case _ => None
  }        
}

object DownLevelLogonName {
  def unapply(str: String): Option[(String, String)] = str.split('\\') match {
    case Array(d, u) if u.length > 0 && d.length > 0 => Some((d, u))
    case _ => None
  }
}

def getDomain(str: String): Option[String] = str match {
  case UserPrincipalName(_, domain) => Some(domain)
  case DownLevelLogonName(domain, _) => Some(domain)
  case _ => None
}

In fact it is possible to create an extractor exhibiting both behaviors by broadening the types it can match:

object UserPrincipalName {
  def unapply(obj: Any): Option[(String, String)] = obj match {
    case upn: UserPrincipalName => Some((upn.username, upn.domain))
    case str: String => str.split('@') match {
      case Array(u, d) if u.length > 0 && d.length > 0 => Some((u, d))
      case _ => None
    }
    case _ => None
  }        
}

In general, extractors are simply a convenient reformulation of the Option pattern, as applied to methods with names like tryParse:

UserPrincipalName.unapply("user@domain") match {
  case Some((u, d)) => ???
  case None => ???
}