1.4k

Introduction to functional programming idioms (e.g. monoids, monads, ...) using Scala

June 12, 2012

## Transcript

4. ### Most people intuitively understand this code: for { x <-

List(1, 2) } yield (x + 2) => List(3, 4)
5. ### And most people intuitively understand this code: for { i

<- 1 until n j <- 1 until (i - 1) if isPrime(i + j) } yield (i, j) => IndexedSeq((4,1), (5,2), (6,1), (7,4), (8,3), (8,5), (9,2), (9,4))
6. ### Yet struggle a bit more with this: // desugared from

previous for-comprehension (1 until n) flatMap { i => (1 until (i – 1)) filter { j => isPrime(i + j) } map { j => (i, j) } } => IndexedSeq((4,1), (5,2), (6,1), (7,4), (8,3), (8,5), (9,2), (9,4))
7. ### And even fewer understand why this doesn't compile: for {

order <- maybeOrder lineItem <- order.lineItems } yield lineItem.product.name [Expected] => Seq(ratchet, hammer) [Actual] => error: type mismatch; found : Seq[Nothing] required: Option[?] lineItem <- order.lineItems ^
8. ### Why Talk About Monoids + Monads? Abstract data types representing

some form of computation Mathematical foundation (category theory) Popular in functional cycles (e.g., Haskell) Learn the jargon so you can understand and not feel (too) intimidated Make your head explode (?!)
9. ### No, really – why bother? Think differently about problems (i.e.,

paradigm shift) Add another tool to your toolset (more power to you!) Refactor + eliminate some boilerplate (through better abstraction) Hopefully, write better programs. (different structuring mechanism for data + flow control)
10. ### Caveat Not all programming languages support monadic programming equally. Some

don't. <cough> Java <cough!> Some do to some extent. <mmmh> Scala <mmmh!> Some pretty much require it. <hmmm> Haskell <hmmm?> Don't try to fit a square peg in a round hole. Don't work against the grain of your language.
11. ### Typeclasses Polymorphism without using object orientation Don't have to think

about types belonging to a big hierarchy of types Instead, think about what the types can act like and then “decorate” them with appropriate typeclasses e.g., an Int can act like a lot of things. It can act like an equatable thing, like an ordered thing, like an enumerable thing, etc. implicit def (x: Int): Equality[Int] = … implicit def (x: Int): Ordered[Int] = … implicit def (x: Int): Enumerable[Int] = … implicit def (x: Int): Numeric[Int] = …
12. ### What the %@#! is a Monoid? Algebraic structure 1) identity

element (Ø) 2) associative binary operation (□) (x: X) □ (Ø: X) ≡ (x: X) (x1: X) □ (x2: X) ≡ (_: X)
13. ### Integers under Addition • identity: 0 (zero) • binary operation:

addition // identity 1 □ 0 => 1 // normal 1 □ 1 => 2
14. ### Integers under Addition • identity: 0 (zero) • binary operation:

addition // identity (1: Plus[Int]) □ (0: Plus[Int]) => (1: Plus[Int]) // normal (1: Plus[Int]) □ (1: Plus[Int]) => (2: Plus[Int])
15. ### Integers under Multiplication • identity: 1 • binary operation: multiplication

// identity 42 □ 1 => 42 // normal 42 □ 2 => 84
16. ### Integers under Multiplication • identity: 1 • binary operation: multiplication

// identity (42: Mult[Int]) □ (1: Mult[Int]) => (42: Mult[Int]) // normal (42: Mult[Int]) □ (2: Mult[Int]) => (84: Mult[Int])
17. ### Other Monoids Strings List[X] Hour on clock Functions SQL aggregates,

e.g., sum(visitors) ...
18. ### Monoid in Scala trait Monoid[T] { def identity: T def

combine(x: T, y: T): T }
19. ### Monoid in Scala  object StringMonoid extends Monoid[String] { def

identity: String = "" def combine(x: String, y: String) = x.concat(y) } object IntMonoid extends Monoid[Int] { def identity: Int = 0 def combine(x: Int, y: Int): Int = x + y }
20. ### Monoid in Scala  A sum method, which works over

arbitrary monoids, def sum[A](xs: Seq[A])(implicit m: Monoid[A]): A = if (xs.isEmpty) m.identity else m.combine(xs.head, sum(m)(xs.tail) And an be called as follows: sum(List("a", "bc", "def")) => “abcdef” sum(List(1, 2, 3)) => 6
21. ### Monoid in Scala  Alternatively, def sum[A](xs: Seq[A])(implicit m: Monoid[A]):

A = xs.foldLeft(m.identity) { _ + _ }
22. ### Mo' Monoids! // booleans under logical disjunction (OR) true true

assert_=== true ⊹ true false assert_=== true ⊹ false true assert_=== true ⊹ false false assert_=== false ⊹ // booleans under logical conjunction (AND) (true | |) (true | |) assert_=== (true | |) ∧ ⊹ ∧ ∧ (true | |) (false | |) assert_=== (false | |) ∧ ⊹ ∧ ∧ (false | |) (true | |) assert_=== (false | |) ∧ ⊹ ∧ ∧ (false | |) (false | |) assert_=== (false | |) ∧ ⊹ ∧ ∧
23. ### Mo' Monoids! // Replicate a value using the List Monoid

and List Pure 42.replicate[List](3) assert_=== List(42, 42, 42)
24. ### Mo' Monoids! // Iterate over a function (Int, Int) =>

(Int, Int) // through the Stream Monoid to generate Fibonacci numbers val fib: Stream[Int] = (0, 1).iterate[Stream] { case (a, b) => (b, a + b) } map (_._1)
25. ### Mo' Monoids! // Unfold a value through the List Monoid

with List Pure 1.unfold[List, String]((a: Int) => (a < 5).option((a.toString, a + 1))) => List("1", "2", "3", "4")
26. ### Generic Functions over Monoids sum() a sequence of values repeat()

a value infinitely replicate() a value n-times unfold() decompose until some condition iterate() generate until some condition ...

28. ### Monads as Containers Put stuff in the container Operate on

values inside the container Flatten containers
29. ### Oh, Blueberries! Let's assume you can create boxes to put

stuff in. object Box { def apply[X](x: X): Box[X] } Now you can put a blueberry in the box, Box.apply(_: Blueberry) => Box[Blueberry]
30. ### Err... I'd rather have Strawberries And you can swap blueberries

for strawberries, val box: Box[Blueberry] val f: Blueberry => Strawberry (box map f) => Box[Strawberry]
31. ### [ Boxification ] If you have functions that return Boxes,

that's fine too. val box: Box[Blueberry] val f: Blueberry => Box[Strawberry] (box flatMap f) => Box[Strawberry]
32. ### Decluttering! You can also flatten your boxes, nestedBoxes: Box[Box[Blueberry] nestedBoxes.flatten

=> Box[Blueberry]

requires the ability to take stuff out of the box! But a few monads do allow you to... e.g. List[_], Option[_], ... One notable monad that insists on keeping everything to itself: IO[_] !
34. ### Monads as Computations Run a computation, including “doing nothing” Combine

two computations X and Y, • Run X, discard result, then run Y • Run X and feed result to Y. * This intuition is generally more useful, but is more difficult to explain, precisely because it is so general.
35. ### Mo' Monads! // bind ∗ List(1, 2, 3) List(_, 7)

=> List(1, 7, 2, 7, 3, 7) ∗

37. ### Destructuring the Monad Put stuff in the container => “Pure”

Operate on values inside the container => “Functor” Flatten containers => characteristic of the Monad
38. ### Pure // Put stuff in the container trait Pure[T[_]] {

def pure[A](a: A): T[A] }
39. ### Functor // Operate on values inside container trait Functor[T[X]] {

def map[Y](f: X => Y): T[Y] }
40. ### Pointed trait Pointed[T[_]] extends Pure[T] // Put stuff in box

with Functor[T] // Operate on items in box

B]): T[B] }
42. ### Monad in Scala trait Monad[T[A]] extends Pure[T], Functor[T] Pointed[T], Applicative[T]

{ def flatMap[B](f: A => T[B]): T[B] }

44. ### Recap: Monoids, Monads Design Patterns • Interfaces with nice compositional

properties • Raise level of abstraction • Lead to code reuse Who knew mathematical abstractions could be useful? :-) But where's the killer app? (Hint: somewhere in your own code)
45. ### Challenges  Code obfuscation? • “Too many types” / “Complex

types” • Crazy #\$^&@! operators • Some abstractions can be difficult to grok Identification can be surprisingly difficult “Natural” or default abstractions aren't necessarily universal Layering monads becomes geometrically complex • Not a silver bullet against complexity
46. ### Challenges  Language support (Scala) • Type inference is somewhat

problematic • Running out of operators • Boilerplate to create/use typeclasses • Performance: boxing overhead • At least we have for-comprehensions ...
47. ### “I believe abstraction can help enormously but can also be

overdone. And everyone has a different limit where more abstraction hurts rather than helps. For instance for me category theory is over the limit but for some others it's fine.” – Martin Odersky, Dec 24th 2009 scala-user@ mailing list
48. ### My Verdict I share Martin Odersky's position that many monads

are generally “over [my] limit” Outside of the usual suspects, need a critical mass of easily identifiable, natural monoids/monads in codebase to warrant using Scalaz Nonetheless, I enjoy seeing Scalaz pushing the functional envelope of Scala I hope the Scala standard library progressively integrate more typeclasses (Equality, Numeric, …) and standardizes functional idioms – in moderation.