{{announcement.body}}
{{announcement.title}}

Basic Guide to Extractors in Scala

DZone 's Guide to

Basic Guide to Extractors in Scala

This blog will guide you through the basic understanding of extractors in Scala and walk you through helpful extractor examples.

· Open Source Zone ·
Free Resource

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

An extractor is an object that has a method. It takes an object as an input and gives back arguments. Custom extractors are created using the unapply method. The method is called extractor because it takes an element of the same set and extracts some of its parts, 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 but also a method that needs to be implemented by an object in order for it to be an extractor.

Java
 




x


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


"apply" method is called while instancing case class: 

Java
 




xxxxxxxxxx
1


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


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

Java
 




xxxxxxxxxx
1


1
val Blog(name,length) = blog


Let's understand extractors using examples:

The return type of an unapply should be chosen while keeping the 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, group them in an optional tuple.

Let's discuss all three points one by one:

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

Single Sub-Value

Java
 




xxxxxxxxxx
1
10


 
1
object Math {
2
  def apply(number: Int): Int = number * number
3
    
4
    def unapply(number: Int): Option[Int] = { 
5
    if (number % 2 == 0) Some(number / 2)
6
      else None
7
      }
8
}
9
 
          
10
val obj = Math (2) 
11
  val objectOne = Math (1) //match error is thrown
12
  obj match { 
13
  case Math (number) => // invoke Math.unapply
14
    println (number) // prints 2 
15
}


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

Several Sub-Values 

Java
 




xxxxxxxxxx
1
10


 
1
object DemoAddr {
2
  
3
  def unapply(addr: String): Option[(String, String, String, String)] = {
4
    val tokens = addr split "\\." 
5
      if (tokens.length == 4)
6
        Some(tokens(0), tokens(1), tokens(2), tokens(3))
7
        else None
8
        }
9
  
10
}
11
 
          
12
"127.0.0.1" match {
13
  case DemoAddri (addr, _,_, _) => println ("matched!!" + addr)
14
} // prints matched!!127


Boolean Value 

Java
 




xxxxxxxxxx
1
10


 
1
def unapply (addr: String): Boolean - {
2
  val tokens = addri split "\\." 
3
    if (tokens.length == 4 && isValid (tokens) ) true
4
      else false
5
      }
6
 
          
7
"127.0.0.1" match {
8
  case DemoAddr () => println ("Valid") // prints Valid 
9
    case _ => println ("Invalid")
10
}


If you have a variable number of argument values, scala gives an extractor method unapplySeq.Let's understand the order in which extractors are called:

Java
 




xxxxxxxxxx
1
10


1
object DemoAddress { 
2
  def unapplySeq(ips: String): Option [Seq[String]] = {
3
    Some(ips split ",") 
4
  }
5
} 
6
 
          
7
"192.168.0.1,192.168.0.2,192.168.0.3,192.168.0.4" match {
8
  case DemoAddress (DemoAddr (a, _,_, _), DemoAddr (b, _,_, _), _*) => println (a + " " + b)
9
    case _ => println ("Invalid IP addresses")
10
} // prints 192 192


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

Java
 




xxxxxxxxxx
1
10


 
1
object EMailValidator {
2
  def apply(user: String, domain: String) = user + "@" + domain
3
    
4
    def unapply(str: String): Option [(String, String)] = {
5
    val parts = str split "@" 
6
      if (parts.length == 2)
7
        Some(parts(0), parts(1))
8
        else None
9
        }
10
}
11
 
          
12
object Twice {
13
  def apply(str: String): String = str + str // concatenates string with itself
14
    def unapply(str: String): Option[String] = {
15
    val length = str.length / 2
16
      val half = str.substring(0, length)
17
      if (half == str.substring(length))
18
        Some(half)
19
        else None
20
        }
21
  
22
  object UpperCase {
23
    def unapply(str: String): Boolean = str.toUpperCase == str // returns true if string is in uppercase else false 
24
  }
25
  
26
  def userTwiceUpper(s: String) = s match {
27
    case EMailValidator(Twice(x@UpperCase()), domain) => "match:" + x + "in domain" + domain 
28
      case => "no match"
29
  }



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

EmailValidator divides "DIDI@hotmail.com" into "DIDI" and "hotmail.com" (user and domain). Twice will be called on the user and the 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!

Topics:
scala

Published at DZone with permission of Jyoti Sachdeva . See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}