Date post: | 26-May-2015 |
Category: |
Technology |
Upload: | kobib9 |
View: | 843 times |
Download: | 5 times |
copyright 2013 Trainologic LTD
Scalaz – The Good Parts
2 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• Scalaz is one of the famous/infamous libraries in Scala.
• In this session we are going to overview some parts of
it.
• But first of all, who are the intended audience?
Scalaz
2
3 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• We assume a certain level of familiarity with Scala
syntax and core libraries.
• You should be comfort with the following declaration:
Know Your Scala
3
numbers.foldRight(List.empty[Int]) {_ :: _}
4 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• If you are a seasoned Haskell programmer, you’ll find
that Scalaz resembles Haskell.
• We are going to look at some features of Scalaz without
discussing Category Theory.
• We will see some of the power of Scalaz without going
into the full definitions and laws of the concepts.
• All of these have their place, but we don’t have time for
it all.
• Let’s start with Type Classes…
Scalaz
4
5 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• First of all let’s make it clear: There is no relationship
between Type Classes and OO classes or idioms!
• They arrive of course from Haskell
• A Type Class defines a set of operations that must be
available on types belonging to this Type Class.
• Type Classes are being used extensively in the core
libraries in Scala, in 3rd parties and in Scalaz.
Type-Classes
5
6 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• The first example of a type class in Scalaz we’re going
to discuss is Equal.
• It provides a type safe equality check.
• In contrast to the built-in Scala’s “==“ method.
• E.g.:
Equal
6
import scalaz._ import Scalaz._ val i1 = 5 //> i1 : Int = 5 val i2 = 6 //> i2 : Int = 6 val s1 = "8" //> s1 : String = 8 i1 == i2 //> res0: Boolean = false i1 == s1 //> res1: Boolean = false i1 === i2 //> res2: Boolean = false // i1 === s1 does not compile
7 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• Well, this looks like a simple implicit conversion that
provides the “===“ type-safe equality check.
• What is the connection to Type Classes here?
• Well, the way to create instances of type classes in
Scala, is to use implicits.
• And you can introduce your own implementations
whenever you want…
Discussion
7
8 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• Consider the following code:
Discussion
8
// doesn’t compile here, no implicit Equal
new java.lang.Thread() === new java.lang.Thread()//
implicit object eqThread extends Equal[java.lang.Thread] {
def equal(a1: Thread, a2: Thread) = a1.getId === a2.getId
}
new java.lang.Thread() === new java.lang.Thread()//> res0: Boolean = false val t1 = new java.lang.Thread() //> t1 : Thread = Thread[Thread-2,5,main] t1 === t1 //> res1: Boolean = true
9 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• The Order typeclass provides, well, ordering.
• E.g.:
Order
9
1 ?|? 2 //> res0: scalaz.Ordering = LT 1 ?|? 2.0 // does not compile
1
0 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• Another useful Type Class is Show:
Show
10
1.show //> res0: scalaz.Cord = 1 "hello".show //> res1: scalaz.Cord = "hello“ // the following line doesn’t compile here
new Thread().show
implicit val showThread = Show.shows[Thread] { _.getName }
new Thread().show //> res2: scalaz.Cord = Thread-0
1
1 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• No more printing “com.bom.mom.X@3249234f”
because we forgot to implement toString().
• It will not compile.
• You can also provide different printing for different
usecases (e.g., verbose mode, admin user, etc…).
Discussion
11
1
2 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• We all favor using immutable data (and if not you
should).
• Especially if we’re into functional programming.
• However, this means that modification results in
creating a new ‘version’ of the object.
• This can be quite cumbersome.
• Let’s take a look at an example…
Lens
12
1
3 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• Simple Lens usage:
Example
13
case class Customer(name: String, age: Int)
case class Stock(name: String, price: Int)
case class StockPortfolio(stocks: Map[String, Stock], customer: Customer)
val john = Customer("John", 30) //> john : lens.Customer = Customer(John,30) val customerAge = Lens.lensu[Customer, Int]((a, value) ⇒ a.copy(age = value), a ⇒ a.age)
val customerName = Lens.lensu[Customer, String]((a, value) ⇒ a.copy(name = value), a ⇒
a.name)
val jane = customerName set (john, "Jane")//> jane : lens.Customer = Customer(Jane,30) val johnNextYear = customerAge set (john, 31) //> johnNextYear : lens.Customer = Customer(John,31) val incCustomerAge = customerAge %= {_+1}
val johnNextYearAgain = incCustomerAge(john) //> johnNextYearAgain : (lens.Customer, Int) = (Customer(John,31),31)
1
4 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• Composition:
Example
14
val sampleStock1 = Stock("Sample1", 500) val sampleStock2 = Stock("Sample2", 500)
val stockPrice = Lens.lensu[Stock, Int]((a, value) ⇒ a.copy(price = value), a ⇒ a.price)
val portfolioCustomer = Lens.lensu[StockPortfolio, Customer]((a, value) ⇒ a.copy(customer =
value), a ⇒ a.customer)
val portfolioStocks = Lens.lensu[StockPortfolio, Map[String, Stock]]((a, value) ⇒ a.copy(stocks
= value), a ⇒ a.stocks)
val portfolio = StockPortfolio(customer = john, stocks = Map("Sample1" -> sampleStock1,
"Sample2" -> sampleStock2))
portfolioStocks at "Sample1" andThen stockPrice set (portfolio, 9999)
//> res0: lens.StockPortfolio = StockPortfolio(Map(Sample1 -> Stock(Sample1,9999), Sample2 -> Stock(Sample2,500)),Customer(John,30))
1
5 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• Scenario: we need to perform validation on some data.
• We want to accumulate the validations and not throw an
exception and break the flow.
• Scalaz provides the Validation class which can be used
for this purpose.
• Let’s see and discuss some examples…
Validation
15
1
6 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• Validation:
Example
16
// Basic Data Types
case class Person(name: String, age: Int, yearsInSchool: Int, citizenship : Boolean)
// Validation Functions
trait PublicInstitue {
def checkEducation(p: Person): Validation[String, Person] = {
if (p.yearsInSchool < 12) "Not enough education".fail else p.success
}
def checkCitizenship(p : Person) : Validation[String, Person] = {
if (p.citizenship) p.success else "Must be a Citizen".fail
}
def checkAge(p : Person) : Validation[String, Person] = {
if (p.age < 18) "Too young".fail else if (p.age > 65) "Too old".fail else p.success
} }
1
7 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• Ok, now let’s use the previous validation logic in a stop-
on-first-failure strategy:
First Usage
17
// let's try to get some persons a job at the Ministry of Wealth:
object MinistryOfHealth extends PublicInstitue {
def applyForJob(p : Person) : Validation[String, String] = {
for {
v1 <- checkEducation(p)
v2 <- checkCitizenship(p)
v3 <- checkAge(p)
} yield s"${v1.name}, you got the job"
}
}
// Failure(Must be a Citizen)
println( MinistryOfHealth applyForJob Person("John", 30, 15, citizenship = false))
// Success(Shraga, you got the job)
println( MinistryOfHealth applyForJob Person("Shraga", 30, 15, citizenship = true))
// Failure(Not enough education) println( MinistryOfHealth applyForJob Person("Dani", 13, 5, citizenship = true))
1
8 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• OK, nice.
• Now lets accumulate the errors:
Accumulation
18
// let's try to get some persons a job at the Ministry of Transportation:
object MinistryOfTransportation extends PublicInstitue {
def applyForJob(p: Person) = {
(checkEducation(p) |@| checkCitizenship(p) |@| checkAge(p)) {
case (_, _, p) ⇒ s"${p.name}, you got the job"
}
}
}
println(MinistryOfTransportation applyForJob Person("John", 30, 15, citizenship = false))
println(MinistryOfTransportation applyForJob Person("Shraga", 30, 15, citizenship = true))
// Failure(Not enough educationToo young) println(MinistryOfTransportation applyForJob Person("Dani", 13, 5, citizenship = true))
1
9 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• First of all, you can use the following line instead of the
|@| operator:
Discussion
19
(checkEducation(p) ⊛ checkCitizenship(p) ⊛ checkAge(p)) {
• Second, concatenating strings is not that useful.
• Let’s use a non empty list on the failure side:
def applyForJob(p: Person) = {
(checkEducation(p).toValidationNel ⊛ checkCitizenship(p).toValidationNel ⊛
checkAge(p).toValidationNel) {
…
}
// Failure(NonEmptyList(Not enough education, Too young))
println(MinistryOfTransportation applyForJob Person("Dani", 13, 5, citizenship = true))
2
0 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• We have seen here the use for ApplicativeBuilder
operator: |@|
• Also known as the “macaulay culkin” operator
ApplicativeBuilder
20
2
1 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• And, what if we have a long sequence of validation
methods?
• We wouldn’t want to type all of them. Right?
• We’re not going to discuss it here, but the following
code will do just fine:
What If?
21
def applyForJob(p: Person) = {
val validations = List(checkEducation _, checkCitizenship _, checkAge _)
validations.traverse[({type l[a] = ValidationNel[String, a]})#l, Person](_ andThen
(_.toValidationNel) apply p) map { case p::_ ⇒ s"${p.name}, you got the job" }
}
2
2 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• Well, the Validation type is very useful compared to the
pre-defined Either type.
• Scalaz also provides the \/ type which is almost like
either, but supports for-comprehensions on the right
projection.
Discussion
22
2
3 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• Well, combinators in Validation was nice.
• Also, for-comprehensions are nice.
• Functors, Applicatives and Monads are very useful.
• And they are depicted in Scalaz by appropriate Type
Classes.
• Let’s do a quick overview…
Discussion
23
2
4 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• The Functor type class include types that can be
mapped on.
• Example:
Functor Type Class
24
def foo[F[_] : Functor](fs : F[Int]): F[Int] = {
fs map {_ + 5}
}
val numbers = List(1,2,3) //> numbers : List[Int] = List(1, 2, 3) foo(numbers) //> res0: List[Int] = List(6, 7, 8) val optInt = some(5) //> optInt : Option[Int] = Some(5) foo(optInt) //> res1: Option[Int] = Some(10)
2
5 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• Applicatives were used in the validation examples.
• Let’s understand them more.
• E.g.:
Applicative
25
val opt1 = some(6) //> opt1 : Option[Int] = Some(6) val opt2 = some(7) //> opt2 : Option[Int] = Some(7)
(opt1).<*>(opt2.map(x => (y : Int) => x + y)) //> res0: Option[Int] = Some(13)
(opt1 |@| opt2) (_+_) //> res0: Option[Int] = Some(13)
2
6 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• We can also lift simple values into the Applicative.
• E.g.:
Lifting into Applicative
26
(opt1 ⊛ opt2 ⊛ 6.point[Option]) (_+_ +_) //> res1: Option[Int] = Some(19)
2
7 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• And there are of course, the famous Monads.
• Used in for-comprehensions.
• Instances: Option, Traversables, Validation, Reader,
Writer, State, IO and many more…
Monads
27
2
8 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• Another nice type, is the Memo type that provides, well,
memoization.
• E.g.:
Memo
28
def delay = {
Thread.sleep(50)
1
}
val fac: Int => Int = {
case 1 => 1
case n => n*fac(n-1)*delay
}
val start = java.lang.System.currentTimeMillis
println(fac(50))
println(fac(10))
println(fac(50))
val end = java.lang.System.currentTimeMillis
println(s"${end-start} millis") // takes around 5000 millis
}
2
9 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• And now, with memoization:
Memo
29
def delay = {
Thread.sleep(50)
1
}
val fac: Int => Int =Memo.mutableHashMapMemo {
case 1 => 1
case n => n*fac(n-1)*delay
}
val start = java.lang.System.currentTimeMillis
println(fac(50))
println(fac(10))
println(fac(50))
val end = java.lang.System.currentTimeMillis
println(s"${end-start} millis") // takes around 2500 millis
}
3
0 copyright 2013 Trainologic LTD
Scalaz – The Good Parts
• There are several memoization strategies, including:
• weakHashMap
• Mutable/immutable maps
• Arrays
Memo
30