extractors & implicit conversions
TRANSCRIPT
Abdhesh [email protected]
Extractors & Implicit conversions
Extractors
update method
Implicit conversions, parameters and implicit context
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)}
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
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") }}
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"
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] = ???
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 }}
Extractor (unapply)
object StudentApp extends App { val Student(number, name, addr) = "B123456, Bob, New Delhi" println(s"Student information=${(number, name, address)}") }
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)}
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) }}
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") }}
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)}
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)
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)..
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
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
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!" }}
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") }}
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 }}
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"}
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"}
Extractor Rules
unapply () method called extractor unapply can’t be overloaded (because it always takes the the same argument).
Implicts
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
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)
Where Implicits are Tried...
○ Implicit Parameters
■ Provide more information to a called method about what the caller wants
■ Especially useful with generic functions
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
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
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.
Rules for Implicit
Explicits-First Rule: Whenever code type checks as it is written,
no implicits are attempted
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
Implicits Methods
The idea is to be able to extend functionality of an existing class with new
methods in a type safe manner.
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)}
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.
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}
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.
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
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!
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.
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")}
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 nonimplicit argument in their constructor.
3. There may not be any method, member or object in scope with the same name as the
implicit class.
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)
}
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}")}
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.
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)}
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)}
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")}
References
http://dailyscala.blogspot.in/
Programming in Scala Book
Thanks.