Pattern Matching & unapply & apply

In Scala, we use pattern matching very often. It’s made by unapply. But because we use this by making case class, I always forget how to make it. I write this post to remind me. All example uploaded in below(gist).

How to make apply & unapply

Normal Pattern Matching

Let’s say that there is a class defined like this.


class Person(val name: String, val age: Int, val weight: Int)

And we can write apply by making companion object.


object Person {
  def apply(name: String, age: Int, weight: Int): Person = new Person(name, age, weight)
}

We can also write unapply in companion object like this.


object Person {
  def apply(name: String, age: Int, weight: Int): Person = new Person(name, age, weight)

  def unapply(arg: Person): Option[(String, Int, Int)] =
    if(arg.age > 20) Some(arg.name, arg.age, arg.weight)
    else None
}

Unapply return Option Tuple. After the operation, if it returns Some, it matches to case class. If it returns None, it does not match. We can use it like this.

val young: Person = Person("Martin Junior", 14, 50)
val old: Person = Person("Martin", 29, 70)

def PatternMatching(person: Person): Unit = person match {
  case Person(name, age, weight) => println(s"name: $name, age: $age, weight: $weight")
  case _ => println(s"${person.name} is under 20")
}

In apply, If age is under 20 it returns None so, young is not matched. And old is matched. Yes! it’s simple! But I have additional Curious.

How about pattern matching partial?

What if I don’t want to expose weight in pattern matching? It’s simple!

class SecretPerson(val name: String, val age: Int, val weight: Int)

object SecretPerson {
  def apply(name: String, age: Int, weight: Int): SecretPerson = new SecretPerson(name, age, weight)

  def unapply(arg: SecretPerson): Option[(String, Int)] =
  if(arg.age >= 20) Some(arg.name, arg.age)
  else None
}

def PatternMatchingSecret(secretPerson: SecretPerson): Unit = secretPerson match {
  case SecretPerson(name, age) => println(s"name: $name, age: $age")
  case _ => println(s"${secretPerson.name} is under 20")
}

val secretYoung: SecretPerson = SecretPerson("Martin Junior", 14, 50)
val secretOld: SecretPerson = SecretPerson("Martin", 29, 70)

PatternMatchingSecret(secretYoung)
PatternMatchingSecret(secretOld)

Just return Option Tuple without weight. It is possible!

Case class unapply overriding

How about case class? It already has pre-defined unapply. What if I also want to hide weight this time? Let’s do it!


case class CasePerson(name: String, age: Int, weight: Int)
object CasePerson {
//  Compile Error - unapply is defined twice
//  def unapply(arg: CasePerson): Option[(String, Int)] =
//    if(arg.age >= 20) Some(arg.name, arg.age)
//    else None
}

val caseYoung: CasePerson = CasePerson("Martin Junior", 14, 50)
val caseOld: CasePerson = CasePerson("Martin", 29, 70)

def PatternMatchingCase(casePerson: CasePerson): Unit = casePerson match {
//   case CasePerson(name, age) => println(s"name: $name, age: $age") compile error: wrong number of arguments
  case CasePerson(name, age, weight) => println(s"name: $name, age: $age, weight: $weight")
  case _ => println(s"${casePerson.name} is under 20")
}

It’s impossible. We can get compile error in override ‘unapply is defined twice’. And also in case matching part ‘wrong number of arguments’.

class Person(val name: String, val age: Int, val weight: Int)
object Person {
def apply(name: String, age: Int, weight: Int): Person = new Person(name, age, weight)
def unapply(arg: Person): Option[(String, Int, Int)] =
if(arg.age >= 20) Some(arg.name, arg.age, arg.weight)
else None
}
val young: Person = Person("Martin Junior", 14, 50)
val old: Person = Person("Martin", 29, 70)
def PatternMatching(person: Person): Unit = person match {
case Person(name, age, weight) => println(s"name: $name, age: $age, weight: $weight")
case _ => println(s"${person.name} is under 20")
}
PatternMatching(young)
PatternMatching(old)
class SecretPerson(val name: String, val age: Int, val weight: Int)
object SecretPerson {
def apply(name: String, age: Int, weight: Int): SecretPerson = new SecretPerson(name, age, weight)
def unapply(arg: SecretPerson): Option[(String, Int)] =
if(arg.age >= 20) Some(arg.name, arg.age)
else None
}
def PatternMatchingSecret(secretPerson: SecretPerson): Unit = secretPerson match {
case SecretPerson(name, age) => println(s"name: $name, age: $age")
case _ => println(s"${secretPerson.name} is under 20")
}
val secretYoung: SecretPerson = SecretPerson("Martin Junior", 14, 50)
val secretOld: SecretPerson = SecretPerson("Martin", 29, 70)
PatternMatchingSecret(secretYoung)
PatternMatchingSecret(secretOld)
case class CasePerson(name: String, age: Int, weight: Int)
object CasePerson {
// Compile Error - unapply is defined twice
// def unapply(arg: CasePerson): Option[(String, Int)] =
// if(arg.age >= 20) Some(arg.name, arg.age)
// else None
}
val caseYoung: CasePerson = CasePerson("Martin Junior", 14, 50)
val caseOld: CasePerson = CasePerson("Martin", 29, 70)
def PatternMatchingCase(casePerson: CasePerson): Unit = casePerson match {
// case CasePerson(name, age) => println(s"name: $name, age: $age") compile error: wrong number of arguments
case CasePerson(name, age, weight) => println(s"name: $name, age: $age, weight: $weight")
case _ => println(s"${casePerson.name} is under 20")
}

Leave a comment