Extractors

File : ExtractorsDemo

When a Java developer sees Pattern Matching for the first time it look like all rules from OOP are broken - all class internals are shown to outside world.

First thing - case classes exists in a very different paradigm than classical java objects

Second thing - topic of todays workshops - you can actually use pattern matching and still have encapsulated private state - how? - extractors

Unapply

Let's have a standard class with private mutable state

class ClassWithPrivateState(initial: String = "") {
  private var mutableState: String = initial

  def add(chars: String): Unit = mutableState = mutableState + chars
  def drop(howMany: Int) = mutableState = mutableState.drop(howMany)

}

It is not a case class so no companion object will be generated. But still we can create our own. We already saw method apply so now time for it brother/sister unapply.

object ClassWithPrivateState {
  def apply(initial: String = ""): ClassWithPrivateState = new ClassWithPrivateState(initial)
  def unapply(arg: ClassWithPrivateState): Option[String] = Some(arg.mutableState)
}

How is this usable? _unapply _returns Option[A] where A is of extracted field type so in the next example :

instance.add("_added")
instance.drop(3)

instance match {
      case ClassWithPrivateState(extracted) => println(s"extracted state $extracted")
}

will display : extracted state tial_added

We did not returned direct reference to private state but we extracted it through unapply. _To see that getter of mutable state and extractor are two seprate things l_et's see another example where we will add pattern matching to a primitive

object SimpleEvenExtractor {
  def unapply(i: Int) = if (i % 2 == 0) Some(i) else None
}

and now we can use this extractor with primitive in pattern matching

 val i = 2
 i match {
   case SimpleEvenExtractor(i) => println(s"$i is even")
   case other => println(s"$other is not even")
 }

This was about filtering and now let's change information format structure

object NameExtractor {
  def unapply(fullName: String): Option[(String, String)] = {
    val parts: Array[String] = fullName.split(" ")
    if (parts.length == 2) Some(parts(0), parts(1)) else None //tuple synthatic sugar!
  }
}

"single" match {
   case NameExtractor(name, surname) => println(s"extracted name=$name, surname=$surname")
   case other => println(s"not a name $other")
}

Extracting Private State

Till now we have just manipulated public state. In the exercises we will see how to treat extractor as intelligent getter which can execute proper get/extraction according to pattern matching.

Exercises

FILE : PatternMatching2Exercises

Exercise1 - Email Extractor

Write simple email extractor which can split string into two parts : name _and _domain

object EmailExtractor {
  def unapply(mail: String): Option[(String, String)] = ???
}

Exercise2 - State History

Hawing class :

class StateHistory {
  private var history: Vector[Int] = Vector()

  def addEvent(e: Int): Unit = history = history.+:(e)

}

Implement extractor which returns number of events if there are any.

Exercise3 - Conditional Extractor

In this exercise you need to write extractor which will 'get' basket content only if it has some content.

So this time unapply method will return Boolean

def unapply(b:Basket) : Boolean = ???

then you will have to implement _process _method to make test passing

result mustBe "processed 2 products"

results matching ""

    No results matching ""