Successfully reported this slideshow.
Upcoming SlideShare
×

# Practical scalaz

7,818 views

Published on

Slides from a presentation entitles "Practical Scalaz" given to the London Scala User Group (lsug) at Skillsmatter on Sept 14th 2011

• Full Name
Comment goes here.

Are you sure you want to Yes No
• Be the first to comment

### Practical scalaz

1. 1. GSA Capital<br />Practical Scalaz(or)How to make your life easier the hard way<br />Chris Marshall Aug 2011<br />@oxbow_lakes<br />
2. 2. Overview of talk<br />The library is confusing<br />What’s with all this maths anyway?<br />The method names are all stupid<br />GSA Capital<br />
3. 3. Where is the love?<br />Kinds<br />M[A] ~> MA[M, A]<br />A ~> Identity[A] <br />M[A, B] ~> MAB[M, A, B]<br />Wrappers <br />OptionW, ListW, BooleanW<br />Data Types<br />Validation<br />NonEmptyList<br />GSA Capital<br />
4. 4. Typeclasses<br />Common “patterns” <br />“retrofitted” in a uniform way to many types<br />Uses implicits to do this<br />...Interfaces<br />Like being able to retrofit an interface onto classes which “logically implement” that interface<br />...Adapters<br />Adapt existing types to our structures<br />GSA Capital<br />
5. 5. Example typeclass<br /> traitEach[-E[_]]{<br />   defeach[A](e: E[A], f: A => Unit): Unit<br /> }<br />GSA Capital<br />implicit def OptionEach: Each[Option] <br /> = new Each[Option] {<br />     def each[A](e: Option[A], f: A => Unit) <br /> = eforeachf<br />   }<br />
6. 6. Monoids<br />There are monoids everywhere<br />A set<br />With an associative operation<br />And an identity under that operation<br />GSA Capital<br />
7. 7. Numbers<br />scala> 1 |+| 2<br />res0: Int3<br />scala> 1.2 |+| 3.4<br />res1: Double 4.6<br />GSA Capital<br />
8. 8. Your own<br />scala> 200.GBP|+|350.GBP<br />res2: oxbow.Money550.00 GBP<br />GSA Capital<br />
9. 9. Monoids Beget Monoids<br />Option[A] is a Monoid<br />if A is a monoid<br />(A, B, .. N) is a Monoid<br />if A, B..N are monoids<br />A => B is a Monoid<br />if B is a Monoid<br />Map[A, B] is a Monoid<br />if B is a Monoid<br />A => A is a monoid<br />Under function composition<br />GSA Capital<br />
10. 10. ...<br />scala> some(4) |+|none[Int]<br />res4: Option[Int] Some(4)<br />scala> none[Int] |+|none[Int]<br />res5: Option[Int]None<br />scala> some(4) |+|some(5)<br />res6: Option[Int]Some(9)<br />scala> (1, “a”, 4.5) |+| (2, “b”, 3.2)<br />res7: (Int, String, Double)(3, “ab”, 7.7)<br />GSA Capital<br />
11. 11. What does this mean?<br />Winning!<br />GSA Capital<br />
12. 12. traitTradingPosition {definventoryPnL(implicitprices: Map[Ticker, Double]) : DoubledeftradingPnL(implicit prices: Map[Ticker, Double]) : Double final def totalPnL(implicitprices: Map[Ticker, Double]) = inventoryPnL->tradingPnL }<br />GSA Capital<br />valpositions: Seq[TradingPosition] = db.latestPositions()val (totalTrad, totalInv) = positions.map(_.totalPnL).asMA.sum<br />
13. 13. traitTradingPosition {definventoryPnL(implicit pxs: Map[Ticker, Double]): Option[Double]deftradingPnL(implicit pxs: Map[Ticker, Double]): Option[Double]final def totalPnL(implicit pxs: Map[Ticker, Double]) = inventoryPnL|+|tradingPnL }<br />GSA Capital<br />valposns: Seq[TradingPosition] = db.latestPositions()valmaybePnL: Option[Double] = posns.map(_.totalPnL).asMA.sum<br />
14. 14. traitTradingPosition {defsym: Tickerdefqty: Int }<br />GSA Capital<br />valpete: Map[Ticker, Int] = positions1.map(p => p.sym -> p.qty).toMapvalfred: Map[Ticker, Int] = positions2.map(p => p.sym->p.qty).toMap<br />
15. 15. valtotalPositions = pete|+|fred<br />GSA Capital<br />for any key, if book1(key) == nand book2(key) == m, then the resulting map hasn |+| mat key<br />Adding across a bunch of Maps now becomes as easy as... <br />allBooks.asMA.sum<br />
16. 16. traitTradingPosition {defsym: Tickerdefqty: Int }type Filter = TradingPosition => Boolean<br />GSA Capital<br />Filters<br />
17. 17. Observe: filters are monoids<br />GSA Capital<br />vallondon: Filter = (_ : TradingPositon).sym.idendsWith“.L”<br />valny: Filter = (_ : TradingPositon).sym.idendsWith“.O”<br />positionsfilter (london|+|ny) <br />
18. 18. Conjunction<br /> typeFilter = TradingPosition => BooleanConjunction<br />GSA Capital<br />vallondon= (t : TradingPositon) => (t.sym.idendsWith“.L”) |∧|<br />valbig = (t : TradingPositon) => (t.qty> 100000) |∧|<br />positionsfilter (london|+|big)<br />
19. 19. Monoid = Semigroup + Zero<br />Monoid is actually split in 2<br />Semigroup (the associative bit)<br />Zero (the identity bit)<br />~ is “or zero” on OptionW<br />A unary method (declared unary_~)<br />It is really useful<br />Eh?<br />GSA Capital<br />
20. 20. varposns: Map[Ticker, Int] = Map.emptydefnewTrade(trd: Trade) {posns += (trd.sym-> ( (posns.get(trd.sym) getOrElse0) + trd.qty)) }<br />GSA Capital<br />But observe the equivalence of the following:<br />(posns.get(trd.sym) getOrElse0) <br />And:<br />~posns.get(trd.sym)<br />
21. 21. GSA Capital<br />defnewTrade(trd: Trade) {posns += (trd.sym-> (~posns.get(trd.sym) |+|trd.qty)) } <br /><ul><li> We can change the value type
22. 22. To Double?
23. 23. To any Monoid!
24. 24. Your own?</li></ul>Is logically...<br />
25. 25. varcharges: Map[Ticker, Money] = Map.emptyimplicit valChargeCcy = Currency.USDdefnewTrade(trd: Trade) {charges += (trd.sym -> ( ~charges.get(trd.sym) |+|trd.charges)) } Where we have defined our own thusimplicit defMoneyZero(implicit ccy: Currency) : Zero[Money] = zero(Money.zero(ccy))implicit valMoneySemigroup: Semigroup[Money] = semigroup(_ add _)<br />GSA Capital<br />
26. 26. BooleanW, OptionW<br />Consistency with ? and |<br />Option[A] | A == getOrElse<br />Boolean ? a | b == ternary<br />Boolean ?? A == raise into zero<br />Boolean !? A == same same but different<br />Boolean guard / prevent<br />GSA Capital<br />
27. 27. Endo<br />An Endo in scalaz is just a function:<br />A => A<br />It’s a translation (e.g. negation)<br />BooleanW plus Zero[Endo]<br /><ul><li>“If this condition holds, apply this transformation”</li></ul>GSA Capital<br />
28. 28. We don’t want to repeat ourselves!for { e <- xml “instruments” f <- e.attribute(“filter”) } yield (if (f == “incl”) new Filter(instr(e)) elsenew Filter(instr(e)).neg)^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <br />GSA Capital<br /><instruments filter=“incl”> <symbol value=“VOD.L” /> <symbol value=“MSFT.O” /> </instruments><br />
29. 29. We can do this...<br />GSA Capital<br />valreverseFilter = EndoTo((_ : Filter).neg)<br />for {e <- xml “instruments”f <- e.attribute(“filter”)<br /> } <br />yield (f == “incl”) !?reverseFilterapplynew Filter(instr(e))<br />
30. 30. Aside: playing around<br />scala> EndoTo(-(_ : Double))<br />res0: scalaz.Endo[Double] scalaz.Endo@6754642<br />scala> true?? res0 apply2.3<br />res1: Double -2.3<br />scala> false?? res0 apply2.3<br />res2: Double 2.3<br />scala> implicitly[Zero[Endo[Double]]]<br /> res3: scalaz.Endo[Double] scalaz.Endo@8ae765<br />GSA Capital<br />
31. 31. Validation<br />Validation is the killer app for me<br />Opened my eyes to how appalling java Exceptions are<br />Let your types do the talking!<br />GSA Capital<br />
32. 32. Aside: composition<br />Functors:<br />M[A] plus A => B equals M[B]<br />Monads<br />M[A] plus A => M[B] equals M[B]<br />Applicative<br />M[A] plus M[A => B] equals M[B]<br />GSA Capital<br />
33. 33. Composing validations<br />//MAP<br />Validation[X, A] ~>A => B~> Validation[X, B]<br />//FLATMAP<br />Validation[X, A] ~> A => Validation[X, B] ~> Validation[X, B]<br />//APPLY<br />Validation[X1, A], Validation[X2, B] ~> (A, B) => C<br />~> Validation[X1 |+| X2, C]<br />GSA Capital<br />
34. 34. ValidationNEL<br />Validation[NonEmptyList[F], S] = ValidationNEL[F, S]<br />scala> “Bah!”.failNel[Int]<br />res1 : scalaz.Validation[NonEmptyList[String], Int]Failure(NonEmptyList(Bah!))<br />scala> 1.successNel[String]<br />res2 : scalaz.Validation[NonEmptyList[String], Int] Success(1)<br />GSA Capital<br />