3

How to use extractors in Scala ?

 2 years ago
source link: https://blog.knoldus.com/extractors-in-scala/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

How to use extractors in Scala ?

Reading Time: 3 minutes

This blog will guide you through the basic understanding of extractors in Scala.

An extractor is an object that has an unapply method. It takes an object as an input and gives back arguments. Custom extractors are created using the unapply method. The unapply method is called extractor because it takes an element of the same set and extracts some of its parts,  apply method also called injection acts as a constructor, takes some arguments and yields an element of a given set.

A case class in Scala, by default implements, apply and unapply methods.

Case Classes are special because Scala automatically creates a companion object for them: a singleton object that contains not only an apply method for creating new instances of the case class but also an unapply method that needs to be implemented by an object in order for it to be an extractor.

xxxxxxxxxx
 case class Blog (name: String, length: Int)

apply”  method is called while instantiating case class :

xxxxxxxxxx
 val blog = Blog("extractor", 140)

val name = Blog.unapply(blog).get is same as:

xxxxxxxxxx
 val Blog(name,length) = blog

Let’s understand extractors using examples:

The return type of an unapply should be chosen while keeping following things in mind:

  1. If it returns a single sub-value of type T, return a Option[T].
  2. If it is just a test, return a Boolean.
  3. If you want to return several sub-values T1,...,Tn, group them in an optional tuple Option[(T1,...,Tn)].

Let’s discuss all three points one by one:

When no value matches in case, match error is thrown.

SINGLE SUB-VALUE

xxxxxxxxxx
 object Math {
def apply(number: Int): Int = number * number
def unapply(number: Int): Option[Int] = {
if (number % 2 == 0) Some(number / 2)
else None
}
}
val obj = Math (2)
val objectOne = Math (1) //match error is thrown
obj match {
case Math (number) => // invoke Math.unapply
println (number) // prints 2
}

val object = Math(2) expands to val object = Math.apply(2)

SEVERAL SUB-VALUES

xxxxxxxxxx
object DemoAddr {
def unapply(addr: String): Option[(String, String, String, String)] = {
val tokens = addr split "\\."
if (tokens.length == 4)
Some(tokens(0), tokens(1), tokens(2), tokens(3))
else None
}
}
"127.0.0.1" match {
case DemoAddr (addr, _, _, _) => println ("matched!!" + addr)
} // prints matched!!127

BOOLEAN VALUE

xxxxxxxxxx
def unapply (addr: String): Boolean = {
val tokens = addr split "\\."
if (tokens.length == 4 && isValid (tokens) ) true
else false
}
"127.0.0.1" match {
case DemoAddr () => println ("Valid") // prints Valid
case _ => println ("Invalid")
}

If you have a variable number of argument values, scala gives an extractor method unapplySeq.

xxxxxxxxxx
object DemoAddresses {
def unapplySeq(ips: String): Option[Seq[String]] = {
Some(ips split ",")
}
}
"192.168.0.1,192.168.0.2,192.168.0.3,192.168.0.4" match {
case DemoAddrress (DemoAddr (a, _, _, _), DemoAddr (b, _, _, _), _*) => println (a + " " + b)
case _ => println ("Invalid IP addresses")
} // prints 192 192

Let’s understand the order in which extractors are called:

xxxxxxxxxx
object EMailValidator {
def apply(user: String, domain: String) = user + "@" + domain
def unapply(str: String): Option[(String, String)] = {
val parts = str split "@"
if (parts.length == 2)
Some(parts(0), parts(1))
else None
}
}
object Twice {
def apply(str: String): String = str + str // concatenates string with itself
def unapply(str: String): Option[String] = {
val length = str.length / 2
val half = str.substring(0, length)
if (half == str.substring(length))
Some(half)
else None
}
object UpperCase {
def unapply(str: String): Boolean = str.toUpperCase == str // returns true if string is in uppercase else false
}
def userTwiceUpper(s: String) = s match {
case EMailValidator(Twice(x@UpperCase()), domain) => "match:" + x + "in domain" + domain
case => "no match"
}

EMailValidator(Twice(x @ UpperCase()), domain) order of calling is from left to right.

EmailValidator divides “[email protected]” into “DIDI” and “hotmail.com” (user and domain). Twice will be called on the user and unapply  method will convert it to “DI”. Uppercase will be called on DI and true will be returned. The result will be:

 match: DI in domain hotmail.com

Extractors do not expose the concrete representation of data. They enable patterns without any relation to the data type for the selected object.

Thanks for reading!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK