Search code examples
javascalacompiler-constructionpattern-matchingextractor

unapply method of a case class is not used by the Scala compiler to do pattern matching, why is that?


abstract class Animal

case class Cat(name: String) extends Animal

case class Dog(name: String) extends Animal

Say I have defined Cat and Dog, two case classes.

Then I use them like this:

val animal = createAnimal
animal match {
  case Dog(anyName) => "this is a dog"
  case Cat("kitty") => "this is a cat named kitty"
  case _ => "other animal"
}

If I decompile the bytecode to Java, I get something like this:

Animal animal = createAnimal();
String result = "other animal";

if (animal instanceof Dog) {
    result = "this is a dog";
} else if (animal instanceof Cat) {
    Cat cat = (Cat) animal;
    if (cat.name() == "kitty") {
        result = "this is a cat named kitty";
    }
}

return result;

The compiler generates unapply methods for both Cat and Dog, but they are not used in the pattern matching code.

Why is that?


Solution

  • Looking at this question from the point of view of the Scala language, the implementation works as specification requires. See http://www.scala-lang.org/docu/files/ScalaReference.pdf

    In §5.3.2, case classes are defined to include an implementation of unapply in the companion (extractor) object.

    However, when we get to pattern matching (§8.1), case classes have their own section on matching, §8.1.6, which specifies their behaviour in pattern matching based on the parameters to the constructor, without any reference to the already-generated unapply/unapplySeq:

    8.1.6 Constructor Patterns

    Syntax:

    SimplePattern ::= StableId ‘(’ [Patterns] ‘)

    A constructor pattern is of the form c(p1,…,pn) where n≥0. It consists of a stable identifier c, followed by element patterns p1,…,pn. The constructor c is a simple or qualified name which denotes a case class. If the case class is monomorphic, then it must conform to the expected type of the pattern, and the formal parameter types of x's primary constructor are taken as the expected types of the element patterns p1,…,pn. If the case class is polymorphic, then its type parameters are instantiated so that the instantiation of c conforms to the expected type of the pattern. The instantiated formal parameter types of c's primary constructor are then taken as the expected types of the component patterns p1,…,pn. The pattern matches all objects created from constructor invocations c(v1,…,vn) where each element pattern pi matches the corresponding value vi.

    The document continues to describe the use of unapply/unapplySeq in §8.1.8; but this is a separate, disjoint, part of the specification, which is applied for classes which are not case classes.

    So you can consider unapply to be a useful method to use in your own code, but not something that is required by pattern matching inside the scala language.