Your SlideShare is downloading.
×

×

Saving this for later?
Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.

Text the download link to your phone

Standard text messaging rates apply

Like this presentation? Why not share!

6,500

Published on

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

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

No Downloads

Total Views

6,500

On Slideshare

0

From Embeds

0

Number of Embeds

2

Shares

0

Downloads

122

Comments

0

Likes

14

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 />

Be the first to comment