How does Scala Match Match determine the head and tail of a list?

How the head and tail are defined in the following statement:

val head::tail = List(1,2,3,4); //head: 1 tail: List(2,3,4) 

There should not be some piece of code that extracts the first element as a chapter and returns the tail as a new list. I combed the standard code of the Scala library, and I can not find / understand how to do this.

+5
source share
1 answer

The Scala construct that is used here is Extractor . The :: symbol is nothing more than the case class in Scala, where there is a unapply method on its companion object to make the extraction of magic happen. Here is a good in-depth guide to extractors. But here is the summary:

Whenever you want to “unzip” the contents of a class, either to bind variables, or as part of a pattern match, the compiler searches for an unapply method for any character on the left side of the expression. It can be an object, a companion object of the case class (for example, :: in your question) or an instance with unapply . The unapply argument is the type of the incoming type to unpack, and the return type is Option of what was declared as the expected structure and types. When matching with a pattern, None indicates that no match was found. When binding variables, a MatchError is raised if the result is None .

A good way to think about unapply is that it is the opposite of apply . Where the unapply receiver of the call function syntax is unapply the receiver of the extractor calls.

To illustrate this further, let's define a simple case class:

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

Since this is the case class, we get the automatically generated apply and unapply on the companion object that look something like this:

 object Cat { // compiler generated... def apply(name: String, age: Int) = new Cat(name, age) def unapply(aCat: Cat): Option[(String, Int)] = Some((aCat.name, aCat.age)) } 

When you create Cat through a companion object, apply is called. When you unpack the components of a Cat , unapply is called:

 val mycat = Cat("freddy", 3) // `apply` called here ... val Cat(name, age) = mycat // `unapply` called here ... val animal: AnyRef = mycat val info = animal match { case Cat(name, age) => "My pet " + name // `unapply` called here case _ => "Not my pet" } // info: String = My pet freddy 

Since unapply returns Option , we have many opportunities to write extractors that handle more interesting cases, for example, by checking if an input type matches some criteria before retrieving the values. For example, let's say we want to get the name of cats that are "old." It can be done:

 object OldCatName { def unapply(aCat: Cat) = if (aCat.age >= 10) Some(aCat.name) else None } 

Usage will be the same as generated unapply :

 val yourcat = Cat("betty", 12) ... val OldCatName(name1) = yourcat // name1: String = "betty" val OldCatName(name2) = mycat // scala.MatchError: Cat(freddy,3) (of class Cat) 

MatchError is not a good thing, so let me use pattern matching:

 val conditions = Seq(mycat, yourcat) map { case OldCatName(oldie) => s"$oldie is old" case Cat(name, age) => s"At age $age $name is not old" } // conditions: Seq[String] = List(At age 3 freddy is not old, betty is old) 

One additional bit of magic associated with the unapply method for :: is that instead of syntactic sugar val ::(head, tail) = ... you can write val head :: tail = ...

0
source

Source: https://habr.com/ru/post/1203019/


All Articles