Slideshare uses cookies to improve functionality and performance, and to provide you with relevant advertising. If you continue browsing the site, you agree to the use of cookies on this website. See our User Agreement and Privacy Policy.

Slideshare uses cookies to improve functionality and performance, and to provide you with relevant advertising. If you continue browsing the site, you agree to the use of cookies on this website. See our Privacy Policy and User Agreement for details.

Like this presentation? Why not share!

7,471 views

Published on

No Downloads

Total views

7,471

On SlideShare

0

From Embeds

0

Number of Embeds

34

Shares

0

Downloads

135

Comments

0

Likes

16

No embeds

No notes for slide

- 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. 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. 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. 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. 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. 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. Numbers<br />scala> 1 |+| 2<br />res0: Int3<br />scala> 1.2 |+| 3.4<br />res1: Double 4.6<br />GSA Capital<br />
- 8. Your own<br />scala> 200.GBP|+|350.GBP<br />res2: oxbow.Money550.00 GBP<br />GSA Capital<br />
- 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. ...<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. What does this mean?<br />Winning!<br />GSA Capital<br />
- 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. 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. 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. 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. traitTradingPosition {defsym: Tickerdefqty: Int }type Filter = TradingPosition => Boolean<br />GSA Capital<br />Filters<br />
- 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. 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. 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. 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. 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. To Double?
- 23. To any Monoid!
- 24. Your own?</li></ul>Is logically...<br />
- 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. 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. 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. 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. 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. 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. 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. 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. 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. 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 />
- 35. Using Validation<br /> deffile(s: String) : Validation[String, File]<br /> deftrades(file: File): List[Trade]<br />GSA Capital<br />valts = file(“C:/tmp/trades.csv”) maptrades<br /> //ts of type Validation[String, List[Trade]]<br />
- 36. More realistically<br />deftrades(f: File): ValidationNEL[String, List[Trade]]<br />GSA Capital<br />file(“C:/tmp/trades.csv”).liftFailNelflatMaptradesmatch {<br />case Failure(msgs) => <br />case Success(trades) =><br /> }<br />
- 37. Using for-comprehensions<br />for {<br /> f <- file(“C:/tmp/trades.csv”).liftFailNel<br />ts <- trades(f)<br /> } yield ts<br />GSA Capital<br />
- 38. What does trades look like?<br />deftrades(f: File): ValidationNEL[String, Trade] = {<br />//List[String]<br />valls = io.Source.fromFile(f).getLines().toList<br />defparse(line: String): Validation[String, Trade]<br /> = sys.error(“TODO”)<br />lsmapparse<<SOMETHING with List[Validation[String, Trade]]>><br /> }<br />GSA Capital<br />
- 39. What does trades look like?<br />deftrades(f: File): ValidationNEL[String, List[Trade]] = {<br />//List[String]<br />valls = io.Source.fromFile(f).getLines().toList<br />defparse(line: String): Validation[String, Trade]<br /> = sys.error(“TODO”)<br /> (lsmap (l => parse(l).liftFailNel))<br /> .sequence[({type l[a]=ValidationNEL[String, a]})#l, Trade]<br /> }<br />GSA Capital<br />
- 40. So why do I care?<br />Your program logic is no longer forked<br />Catching exceptions<br />Throwing exceptions<br />Ability to compose<br />Via map, flatMap and applicative<br />Keeps signatures simple<br />Accumulate errors<br />GSA Capital<br />
- 41. Aside: other applicatives<br /> List[Promise[A]].sequence<br />~> Promise[List[A]]<br /> f: (A, B) => C<br /> (Promise[A] |@| Promise[B]) applyf<br />~> Promise[C]<br />GSA Capital<br />
- 42. Far too much stuff<br />Iteratees<br />Kleisli<br />F: A => M[B]<br />If M is a functor and I have g : B => C, then I should be able to compose these<br />If M is a Monad and I have h : B => M[C] etc<br />Arrows<br />Useful methods for applying functions across data structures, like pairs<br />GSA Capital<br />
- 43. And More<br />IO<br />Deferring side effects<br />Writers<br />Logging<br />Readers<br />Configuration<br />Typesafe equals <br />HeikoSeeberger at scaladays<br />GSA Capital<br />
- 44. Great references<br />Tony Morris’ blog<br />Runar & Mark Harrah’s Apocalisp<br />Nick Partridge’s Deriving Scalaz<br />Jason Zaugg at scalaeXchange<br />GSA Capital<br />

No public clipboards found for this slide

Be the first to comment