concept unapply in category scala

appears as: unapply
Get Programming with Scala MEAP V07

This is an excerpt from Manning's book Get Programming with Scala MEAP V07.

You have discovered that the apply method is a static function to construct class instances. You may wonder if there is a complementary method to it to deconstruct a class into its parameters: this is the unapply method.

In a similar way to the apply method, unapply is a static function that lives in the companion object of a class: it takes an instance class, and it returns its decomposed representation. For example, you could identify a Person by providing a pair with its name and age. The unapply function allows you to use pattern matching, a powerful and expressive tool of the Scala toolbox to match conditions (e.g., age bigger than 18) based on the decomposition of a class: you are going to learn about this in the next unit.

We will talk about the unapply method later in the book again when you are going to learn about case classes and the type Tuple.

Figure 11.2: A syntax diagram for companion objects and their use together with a visual representation of the duality between the methods apply and unapply.

In the previous lessons, you have discovered how to handle nullable values using the type Option. In this lesson, you will learn about tuples, one of the most basic data structures that Scala has to offer to quickly group data in a given order. You will then combine what you have seen about tuples and Option to discuss the unapply method. The function unapply is complementary to apply: while you can use apply to create an instance of a class, unapply allows you to extract information from it. Pattern matching is one of the most powerful tools you can use. So far, you have seen that you can pattern match over raw values (e.g., string, integers, doubles), objects, and case classes. By defining an unapply method for a class, you will also be able to pattern match on them. In the capstone, you will use tuples to group data together, and you will define unapply methods to pattern match over classes without exposing sensitive information.

Let’s see how defining an unapply method for an instance of Drink could improve the readability of your code:

Listing 25.4: Analyzing an instance of Drink using unapply
/* Defining a companion object */
 
object Drink { //#A
 
  def unapply(drink: Drink): Option[(Double, Double)] = { //#B
    val nutritionFacts = drink.loadNutritionFacts()
    Some((nutritionFacts.saturatedFat, nutritionFacts.sugars))
  }
}
 
sealed trait Label
case object LowSaturatedFatAndSugar extends Label
case object LowSaturatedFat extends Label
case object LowSugar extends Label
case object HighSaturatedFatAndSugar extends Label
 
private val fatThreshold: Double = ???
private val sugarThreshold: Double = ???
 
def analyze(drink: Drink): Label = drink match { //#C
  case Drink(fat, sugar) 
if fat < fatThreshold && 
sugar < sugarThreshold => LowSaturatedFatAndSugar
  case Drink(fat, _) if fat < fatThreshold  => LowSaturatedFat
  case Drink(_, sugar) if sugar < sugarThreshold  => LowSugar
  case _  => HighSaturatedFatAndSugar
}

The unapply method in the companion object Drink defines that when decomposing an instance of Drink, the compiler needs to expose only its values for saturated fat and sugar. By doing so, you can simplify your code by forgetting about lots of details when pattern matching: you do not need to remember that your program needs to load the nutrition facts in a later step from another source and that they contain many other fields. You can also refer to saturated fat as “fat” as the ambiguity between total fat and saturated fat is no longer possible.

You usually define the unapply method in the companion object of a class. It always returns a nullable tuple: the tuple represents the information to extract, while Option gives you the opportunity to select no data for specific instances of a type.

The compiler automatically implements an unapply method for case classes by extracting all its fields in order of declaration. For example, consider the following case class:

case class Person(name: String,  age: Int)

Have a look at listing 25.5 for the compile-time-generated implementation of its unapply method.

sitemap

Unable to load book!

The book could not be loaded.

(try again in a couple of minutes)

manning.com homepage
test yourself with a liveTest