Search code examples
scalapattern-matchingunapply

Scala unapply method


I am trying to understand the scala unapply method.

Below is my understanding. Say if I have a Person object:

class Person(val fname: String, val lname: String)

object Person{
  def unapply(x: Person) : Option[(String, String)] = 
    Some(x.fname,x.lname)
}

new Person("Magic", "Mike") match {
  case Person(x, y) => s"Last Name is ${y}"
  case _ => "Unknown"
}

This i presume the case calls something like:

val temp = Person.unapply(new Person("Magic", "Mike"))
if (temp != None)  { val (x, y) = temp.get }
else { <go to next case> }

but how does below unapply work when i have like below:

new Person("Magic", "Mike") match {
  case Person("Harold", y) => s"Last Name is ${y}"
  case Person("Magic", y) => s"Last Name is ${y}"
  case _ => "Unknown"
}

How does it access the value of fname("Magic") in unapply method and give me the same/correct result as the first one?


Solution

  • Running scalac with -Xprint:patmat will show you how syntactic trees look after pattern matching phase:

    scalac -Xprint:patmat test.scala
    
      case <synthetic> val x1: Person = new Person("Magic", "Mike");
      case10(){
        <synthetic> val o12: Option[(String, String)] = Person.unapply(x1);
        if (o12.isEmpty.unary_!)
          {
            <synthetic> val p3: String = o12.get._1;
            val y: String = o12.get._2;
            if ("Harold".==(p3))
              matchEnd9(scala.StringContext.apply("Last Name is ", "").s(y))
            else
              case11()
          }
        else
          case11()
      };
      case11(){
        <synthetic> val o14: Option[(String, String)] = Person.unapply(x1);
        if (o14.isEmpty.unary_!)
          {
            <synthetic> val p5: String = o14.get._1;
            val y: String = o14.get._2;
            if ("Magic".==(p5))
              matchEnd9(scala.StringContext.apply("Last Name is ", "").s(y))
            else
              case13()
          }
        else
          case13()
      };
      case13(){
        matchEnd9("Unknown")
      };
    

    As you can see, for each case first it calls unapply on the matched object, then if Option is not empty (so it has matched), it checks if one of the elements of tuple is equal to expected value, if so then it matches to the closure for this case.