Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

  • 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 />
  35. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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 />

×