extractors & implicit conversions

50
Abdhesh Kumar [email protected] Extractors & Implicit conversions

Upload: knoldus-software-llp

Post on 15-Jul-2015

1.071 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Extractors & Implicit conversions

Abdhesh [email protected]

Extractors & Implicit conversions

Page 2: Extractors & Implicit conversions

Extractors

update method

Implicit conversions, parameters and implicit context

Page 3: Extractors & Implicit conversions

ExtractorPattern Matching???

object ListExtractor { val xs = 3 :: 6 :: 12 :: Nil xs match { case List(a, b) => a * b case List(a, b, c) => a + b + c case _ => 0 }}

object knolx { val head :: tail = List(1, 2, 3)}

Page 4: Extractors & Implicit conversions

Extractor

If you have some student data, each is "B123456, Bob, New Delhi" format, you want to Obtained a student number, name and place of birth information

Page 5: Extractors & Implicit conversions

object Student { def separate(s: String) = { val parts = s.split(",") if (parts.length == 3) Some((parts(0), parts(1), parts(2)) else None }}

object StudentApp extends App { val student = Student.separate("B123456, Bob, New Delhi") student match { case Some((number, name, addr)) => println(s"Student Information:::name=$name, number=$number and address=$addr") case None => println("Wrong data found") }}

Page 6: Extractors & Implicit conversions

val students = List("B123456,Ankit,New Delhi", "B123455,Anand,New Delhi","B123454,Satendra,New Delhi")

This is possible??val Student(number, name, addr) = "B123456, Bob, New Delhi"

Page 7: Extractors & Implicit conversions

Extractor (unapply)

case class Student(number: String, name: String, address: String) val students = List("B123456,Ankit,New Delhi", "B123455,Anand,New Delhi","B123454,Satendra,New Delhi")

val info: List[Student] = ???

Page 8: Extractors & Implicit conversions

Extractor (unapply)

def unapply(object: S): Option[T]

object Student { def unapply(str: String): Option[(String, String, String)] = { val parts = str.split(",") if (parts.length == 3) Some(parts(0), parts(1), parts(2)) else None }}

Page 9: Extractors & Implicit conversions

Extractor (unapply)

object StudentApp extends App { val Student(number, name, addr) = "B123456, Bob, New Delhi" println(s"Student information=${(number, name, address)}") }

Page 10: Extractors & Implicit conversions

Extractor (unapply)

object StudentApp extends App { val students = List( "B123456,Ankit,New Delhi", "B123455,Anand,New Delhi", "B123454,Satendra,New Delhi")

val result: List[Student] = students collect { case Student(number, name, address) => Student(number, name, address) } println(result)}

Page 11: Extractors & Implicit conversions

Extractor (unapply)

case class Student(number: String, name: String, address: String)

object Student { def unapply(str: String): Option[(String, String, String)] = { val parts = str.split(",") if (parts.length == 3) Some(parts(0), parts(1), parts(2)) else None }}

object Utility {

def extractInformation(data: List[String]): List[Student] = { def extractInfo(extractedInfo: List[Student], result: List[String]): List[Student] = { result match { case Student(number, name, address) :: tail => extractInfo(extractedInfo :+ Student(number, name, address), tail) case Nil => extractedInfo case _ :: tail => extractInfo(extractedInfo, tail) } } extractInfo(List[Student](), data) }}

Page 12: Extractors & Implicit conversions

Safe Extractor (unapply)

object EMail { // The extraction method (mandatory) def unapply(str: String): Option[(String, String)] = { val parts = str split "@" if (parts.length == 2) Some(parts(0), parts(1)) else None }}

object SafeExtractor extends App { val any: Any = "[email protected]" any match { case EMail(user, domain) => println(s"Matched:::${(user, domain)}") case _ => println("Not Mathed") }}

Page 13: Extractors & Implicit conversions

Extractor (unapply)

object GivenNames { def unapply(name: String): Option[(String, String)] = { val names = name.trim.split(",") if (names.length >= 2) Some((names(1), names(0))) else None }}

object ExtractNames extends App { def greetWithFirstName(name: String) = name match { case GivenNames(firstName, _) => "Good evening, " + firstName + "!" case _ => "Welcome! Please make sure to fill in your name!" }

val information = "Kumar,Abdhesh,New Delhi" val result = greetWithFirstName(information) println(result)}

Page 14: Extractors & Implicit conversions

object UserName { def unapplySeq(email: String): Option[Seq[String]] = { val parts = email split "," if (parts.nonEmpty) Some(parts) else None }}

object UserNameApp extends App { val name = "Kumar,Abdhesh,New Delhi" name match { case UserName(lastName, firstName, _*) => println(s"First Name=$firstName and Last Name=$lastName") case _ => println("Not Matched") }

val UserName(lastName, firstName, others @ _*) = name}

Extract sequences (unapplySeq)

Page 15: Extractors & Implicit conversions

object UserName { def unapplySeq(email: String): Option[Seq[String]] = { val parts = email split "," if (parts.nonEmpty) Some(parts) else None }}

object UserNameApp extends App { val name = "Kumar,Abdhesh,New Delhi" name match { case UserName(lastName, firstName, others @ _*) => println(s"First Name=$firstName and Last Name=$lastName") case _ => println("Not Matched") }}

Extract sequences (unapplySeq)..

Page 16: Extractors & Implicit conversions

Wildcard operators

If you only care about the first variable you can use _* to reference the rest of the sequence that you don't care about

using @ _* we can get the rest of the sequence assigned to others

Page 17: Extractors & Implicit conversions

Combining fixed and variable parameter extraction

Sometimes, you have certain fixed values to be extracted that you know about at compile time, plus an additional optional sequence of values.

def unapplySeq(object: S): Option[(T1, .., Tn-1, Seq[T])]

*unapplySeq can also return an Option of a TupleN , where the last element of the tuple must be the

Page 18: Extractors & Implicit conversions

Combining fixed and variable parameter extraction

object Names { def unapplySeq(name: String): Option[(String, String, Seq[String])] = { val names = name.trim.split(" ") if (names.size < 2) None else Some((names.last, names.head, names.drop(1).dropRight(1))) }}

object MixedExtractor { def greet(fullName: String) = fullName match { case Names(lastName, firstName, _*) => "Good morning, " + firstName + " " + lastName + "!" Case _ => "Welcome! Please make sure to fill in your name!" }}

Page 19: Extractors & Implicit conversions

A Boolean Extractor

object StudentBool { def unapply(student: String): Boolean = student.contains("Allahabad")}

object BooleanExtractorApp extends App { val name = "B123456,Ankit,Allahabad" name match { case studentI @ StudentBool() => println(studentI) case _ => println("Not Found") }}

Page 20: Extractors & Implicit conversions

A Boolean Extractor

object StudentBool { def unapply(student: String): Boolean = student.contains("Allahabad")}

object BooleanExtractorApp extends App { val students = List("B123455,Anand,New Delhi","B123454,Satendra,New Delhi","B123456,Ankit,Allahabad")

students collect { case std @ StudentBool() => std }}

Page 21: Extractors & Implicit conversions

Instances of a class with unapply method

class Splitter(sep: Char) {def unapply(v: String): Option[(String, String)] = (v indexOf sep) match { case x if (x > 0) => Some((v take x, v drop x + 1)) case _ => None }}

object SimpleExtractor extends App { val SplitOnComma = new Splitter(',') "1,2" match { case SplitOnComma(one, two) => println(one, two) } //All extractors can also be used in assignments val SplitOnComma(one, two) = "1,2"}

Page 22: Extractors & Implicit conversions

Every Regex object in Scala has an extractor

object RegexExtractor extends App {

val Decimal = new Regex("""(-)?(\d+)(\.\d*)?""") val input = "for -1.0 to 99 by 3"

for (s <- Decimal findAllIn input) println _ val dec = Decimal findFirstIn input val Decimal(sign, integerpart, decimalpart) = "-1.23" println((sign, integerpart, decimalpart)) val Decimal(signI, integerpartI, decimalpartI) = "32323"

// Regex to split a date in the format Y/M/D. val regex = "(\\d+)/(\\d+)/(\\d+)".r val regex(year, month, day) = "2010/1/13"}

Page 23: Extractors & Implicit conversions

Extractor Rules

unapply () method called extractor unapply can’t be overloaded (because it always takes the the same argument).

Page 24: Extractors & Implicit conversions

Implicts

Page 25: Extractors & Implicit conversions

Where Implicits are Tried

Implicits are used in three places in Scala

○ Conversions to an expected type

■ For objects passed in to a function that expects a different type

Page 26: Extractors & Implicit conversions

Where Implicits are Tried..

○ Conversions of the receiver of a selection

■ For when methods are called on an object that doesn't have those methods defined, but an implicit conversion does

(e.g. -> method for maps)

Page 27: Extractors & Implicit conversions

Where Implicits are Tried...

○ Implicit Parameters

■ Provide more information to a called method about what the caller wants

■ Especially useful with generic functions

Page 28: Extractors & Implicit conversions

Rules for Implicit

Marking Rule: Only definitions marked implicit are available

The implicit keyword is used to mark which declarations the

compiler may use as implicits.

implicit def intToString(x: Int) = x.toString def dbl2Str(d: Double): String = d.toString // ignored

Page 29: Extractors & Implicit conversions

Rules for Implicit

Scope Rule: An inserted implicit conversion must be in

scope as a single identifier, or be associated with the source

or target type of the conversion.

The Scala compiler will only consider implicit conversions that are in scope

The compiler will also look for implicit definitions in the companion object of

the source or expected target types of the conversion

Page 30: Extractors & Implicit conversions

Rules for Implicit

Non-Ambiguity Rule: An implicit conversion is only inserted if

there is no other possible conversion to insert.

One-at-a-time Rule: Only one implicit is tried.

The compiler will never rewrite x + y to convert1(convert2(x)) + y.

Page 31: Extractors & Implicit conversions

Rules for Implicit

Explicits-First Rule: Whenever code type checks as it is written,

no implicits are attempted

Page 32: Extractors & Implicit conversions

Naming Conventions for Implicit

Can have arbitrary names

The compiler doesn't care

Naming should be chosen for two considerations:

Explicit usage of the conversion

Explicit importing into scope for the conversion

Page 33: Extractors & Implicit conversions

Implicits Methods

The idea is to be able to extend functionality of an existing class with new

methods in a type safe manner.

Page 34: Extractors & Implicit conversions

Implicits Methods

class MyInteger(val i: Int) { def myNewMethod = println("hello from myNewMethod") def inc = new MyInteger(i + 1)}

object MyIntegerConvertor { implicit def int2MyInt(i: Int) = new MyInteger(i) implicit def myInt2Int(mI: MyInteger) = mI.i}

object ImplicitMethods extends App { import MyIntegerConvertor._

val result = 1.myNewMethod val inc = 2.inc def takesInt(i: Int) = println(i) takesInt(inc) takesInt(5.inc)}

Page 35: Extractors & Implicit conversions

Implicits Parameters

It is similar to default parameters, but it has a different

mechanism for finding the “default” value.

The implicit parameter is a parameter to method or constructor that is

marked as implicit.

This means if a parameter value is not mentioned then the

compiler will search for an “implicit” value defined within a scope.

Page 36: Extractors & Implicit conversions

Implicits Parameters

object ImplicitParameters { def p(implicit i: Int) = print(i) def sayThings(implicit args: List[Any]) = args.foreach(println(_))}

object ImplicitParametersApp extends App { import ImplicitParameters._ implicit val nothingNiceToSay: List[Any] = Nil sayThings implicit val v = 2}

Page 37: Extractors & Implicit conversions

Implicits Parameters

Matching implicit arguments Implicits are totally typesafe, and are selected based on the static type of

the

arguments. Here are some examples to show how things work.

def speakImplicitly(implicit greeting: String) = println(greeting)

implicit val aUnit = ()

speakImplicitly //no implicit argument matching parameter type String was found.

Page 38: Extractors & Implicit conversions

Implicits Parameters

Implicit arguments of subtypes

def sayThings(implicit args: List[Any]) = args.foreach(println(_)) implicit val nothingNiceToSay: List[Any] = Nil

sayThings

implicit val hello: List[String] = List("Hello world");

sayThings

Page 39: Extractors & Implicit conversions

Implicits Parameters

/**One important restriction is that there can only be a single implicit keyword per method. It must be at the start of a parameter list (which also makes all values of that parameter list be implicit).*/

def pp(a: Int)(implicit i: Int) = println(a, i)

//both i and b are implicit def pp(a: Int)(implicit i: Int, b: Long) = println(a, i, b)

def pp(implicit i: Int, b: Long) = println(i, b)

def pp(implicit i: Int, b: Long*) = println(i, b)

def pp(b: Long*)(implicit i: Int) = println(i, b)

/When you have a implicit keyword in the first of parameter list, all the parameters in this list are implicit!

Page 40: Extractors & Implicit conversions

Implicit Classes

Must take exactly one parameter (Though can have a second, implicit parameter list)

Must be defined where a method could be defined (not at top level)

Generated method will have same name as the class, so can be imported together.

Page 41: Extractors & Implicit conversions

Implicit Classes

object Helpers { implicit class IntWithTimes(x: Int) { def times[A](f: => A): Unit = { def loop(current: Int): Unit = if (current > 0) { f loop(current - 1) } loop(x) } }}

object ImpliciClasses { import Helpers._

5 times println("HI")}

Page 42: Extractors & Implicit conversions

Implicit Classes

Implicit classes have the following restrictions:

1. They must be defined inside of another trait / class / object .

2.They may only take one non­implicit argument in their constructor.

3. There may not be any method, member or object in scope with the same name as the

implicit class.

Page 43: Extractors & Implicit conversions

Sort User Defined Objects

case class Employee(id: Int, firstName: String, lastName: String)

object Employee { implicit def orderingByName[A <: Employee]: Ordering[A] = Ordering.by(e => e.firstName)

val orderingById: Ordering[Employee] = Ordering.by(e => e.id)

}

Page 44: Extractors & Implicit conversions

Sort User Defined Objects

object ImplicitBox extends App { val employees = List( Employee(1, "A", "AA"), Employee(4, "D", "AA"), Employee(5, "E", "EE"), Employee(2, "B", "BB"), Employee(3, "C", "CC"))

val sortByName = employees.sorted println(s"Sort Employee by name=${sortByName}")

val sortById = employees.sorted(Employee.orderingById) println(s"Sort Employee by Id=${sortById}")}

Page 45: Extractors & Implicit conversions

How can I chain/nest implicit conversions?

Scala does not allow two such implicit conversions taking place, however, so one cannot got from A to C using an implicit A to B and another implicit B to C .

Is there a way around this restriction?

Scala has a restriction on automatic conversions to add a method, which is that it won’t apply more than one conversion in trying to find methods.

Page 46: Extractors & Implicit conversions

How can I chain/nest implicit conversions?

object T1 { implicit def toA(n: Int): A = new A(n) implicit def aToB(a: A): B = new B(a.n, a.n) implicit def bToC(b: B): C = new C(b.m, b.n, b.m + b.n) // won't work //println(5.total) //println(new A(5).total) // works println(new B(5, 5).total) println(new C(5, 5, 10).total)}

Page 47: Extractors & Implicit conversions

How can I chain/nest implicit conversions?

// def m[A <% B](m: A) is the same thing as// def m[A](m: A)(implicit ev: A => B)

object T2 extends App { implicit def toA(n: Int): A = new A(n) implicit def aToB[A1 <% A](a: A1): B = new B(a.n, a.n) implicit def bToC[B1 <% B](b: B1): C = new C(b.m, b.n, b.m + b.n)

// works println(5.total) println(new A(5).total) println(new B(5, 5).total) println(new C(5, 5, 10).total)}

Page 48: Extractors & Implicit conversions

update method

a(x) = y, the compiler interprets this to as a.update(x, y)

class PhonebookExpend {private val numbers = scala.collection.mutable.Map[String, (Int, Int)]() def apply(name: String) = numbers(name) def update(name: String, number: (Int, Int)) = numbers(name) = number}

object EchoUpdate extends App { val e = new Echo() e("hello", "hi") = "knoldus" //e("hello") = ("salut", "bonjour")

val book2 = new PhonebookExpend()

book2("jesse") = (123, 4567890) val (areaCode, local) = book2("jesse")}

Page 49: Extractors & Implicit conversions

References

http://daily­scala.blogspot.in/

Programming in Scala Book

Page 50: Extractors & Implicit conversions

Thanks.