Slide 1

Slide 1 text

Functional Programming Awesomeness Markus Hauck (@markus1189) Scala Enthusiasts Rhein Main, 2017

Slide 2

Slide 2 text

Intro Composition Monoids Errors Conclusion Introduction • FP becomes more popular (finally!) • Time to have a look • There are many useful patterns • Monoids • Better Error Handling Markus Hauck (@markus1189) Functional Programming Awesomeness 2 / 41

Slide 3

Slide 3 text

Intro Composition Monoids Errors Conclusion Lego vs Duplo pictures from shop.lego.com Markus Hauck (@markus1189) Functional Programming Awesomeness 3 / 41

Slide 4

Slide 4 text

Intro Composition Monoids Errors Conclusion Lego vs Duplo pictures from shop.lego.com Markus Hauck (@markus1189) Functional Programming Awesomeness 4 / 41

Slide 5

Slide 5 text

Intro Composition Monoids Errors Conclusion Lego vs Duplo • Duplo favours large specialized building blocks • blocks tend to be too big • limited reuse Markus Hauck (@markus1189) Functional Programming Awesomeness 5 / 41

Slide 6

Slide 6 text

Intro Composition Monoids Errors Conclusion Lego vs Duplo • Duplo favours large specialized building blocks • blocks tend to be too big • limited reuse • Lego focuses on small composable building blocks • blocks can conveniently be reused for other purposes • limited use of specialized building blocks Markus Hauck (@markus1189) Functional Programming Awesomeness 5 / 41

Slide 7

Slide 7 text

Intro Composition Monoids Errors Conclusion Lego vs Duplo • Duplo favours large specialized building blocks • blocks tend to be too big • limited reuse • Lego focuses on small composable building blocks • blocks can conveniently be reused for other purposes • limited use of specialized building blocks OO tends to be like Duplo, FP tends to be like Lego Markus Hauck (@markus1189) Functional Programming Awesomeness 5 / 41

Slide 8

Slide 8 text

Intro Composition Monoids Errors Conclusion Monoids • intuition: “combine stuff” • you can create values from thin air via Monoid.empty • combine two values via Monoid.combine / |+| • additionally: laws (don’t write buggy implementations) Markus Hauck (@markus1189) Functional Programming Awesomeness 6 / 41

Slide 9

Slide 9 text

Intro Composition Monoids Errors Conclusion Monoid Laws // 1) left identity empty |+| x === x // 2) right identity x |+| empty === x // 3) associative x |+| (y |+| z) === (x |+| y) |+| z Markus Hauck (@markus1189) Functional Programming Awesomeness 7 / 41

Slide 10

Slide 10 text

Intro Composition Monoids Errors Conclusion Monoid Typeclass trait Monoid[A] { def empty: A def combine(lhs: A, rhs: A): A // infix operator: |+| } implicit val plus: Monoid[Int] = new Monoid[Int] { def empty: Int = 0 def combine(lhs: Int, rhs: Int): Int = lhs + rhs } object Monoid { def apply[A:Monoid]: Monoid[A] = implicitly } Markus Hauck (@markus1189) Functional Programming Awesomeness 8 / 41

Slide 11

Slide 11 text

Intro Composition Monoids Errors Conclusion Using Our Monoid > Monoid[Int].empty 0 > Monoid[Int].combine(1,2) 3 > 1 |+| 2 3 > 42 |+| Monoid[Int].empty 42 > List(1,2,3).foldLeft(Monoid[Int].empty)(_ |+| _) 6 Markus Hauck (@markus1189) Functional Programming Awesomeness 9 / 41

Slide 12

Slide 12 text

Intro Composition Monoids Errors Conclusion More Monoids • Monoid instance not unique: addition / multiplication / min / max • most collections are Monoids: List / Vector / Set • let’s see some more examples Markus Hauck (@markus1189) Functional Programming Awesomeness 10 / 41

Slide 13

Slide 13 text

Intro Composition Monoids Errors Conclusion Monoid Zoo List[A] is a Monoid Markus Hauck (@markus1189) Functional Programming Awesomeness 11 / 41

Slide 14

Slide 14 text

Intro Composition Monoids Errors Conclusion Monoid Zoo List[A] is a Monoid A => B if B is a Monoid Markus Hauck (@markus1189) Functional Programming Awesomeness 11 / 41

Slide 15

Slide 15 text

Intro Composition Monoids Errors Conclusion Monoid Zoo List[A] is a Monoid A => B if B is a Monoid (A,B) if A and B are Monoids Markus Hauck (@markus1189) Functional Programming Awesomeness 11 / 41

Slide 16

Slide 16 text

Intro Composition Monoids Errors Conclusion Monoid Zoo List[A] is a Monoid A => B if B is a Monoid (A,B) if A and B are Monoids Future[A] if A is a Monoid Markus Hauck (@markus1189) Functional Programming Awesomeness 11 / 41

Slide 17

Slide 17 text

Intro Composition Monoids Errors Conclusion Monoid Zoo List[A] is a Monoid A => B if B is a Monoid (A,B) if A and B are Monoids Future[A] if A is a Monoid Map[A,B] if B is a Monoid Markus Hauck (@markus1189) Functional Programming Awesomeness 11 / 41

Slide 18

Slide 18 text

Intro Composition Monoids Errors Conclusion Monoid Zoo List[A] is a Monoid A => B if B is a Monoid (A,B) if A and B are Monoids Future[A] if A is a Monoid Map[A,B] if B is a Monoid val m1 = Map("as" -> 21, "bs" -> 4) val m2 = Map("as" -> 21, "cs" -> 2) m1 |+| m2 // Map("as" -> 42, "bs" -> 4, "cs" -> 2) Markus Hauck (@markus1189) Functional Programming Awesomeness 11 / 41

Slide 19

Slide 19 text

Intro Composition Monoids Errors Conclusion Monoids Compose (Lego Principle) Config => A Markus Hauck (@markus1189) Functional Programming Awesomeness 12 / 41

Slide 20

Slide 20 text

Intro Composition Monoids Errors Conclusion Monoids Compose (Lego Principle) Config => A Config => Future[A] Markus Hauck (@markus1189) Functional Programming Awesomeness 12 / 41

Slide 21

Slide 21 text

Intro Composition Monoids Errors Conclusion Monoids Compose (Lego Principle) Config => A Config => Future[A] Config => Future[Map[String,A]] Markus Hauck (@markus1189) Functional Programming Awesomeness 12 / 41

Slide 22

Slide 22 text

Intro Composition Monoids Errors Conclusion Monoids Compose (Lego Principle) Config => A Config => Future[A] Config => Future[Map[String,A]] Config => Future[Map[String,(A,B)]] Markus Hauck (@markus1189) Functional Programming Awesomeness 12 / 41

Slide 23

Slide 23 text

Intro Composition Monoids Errors Conclusion Monoids Compose (Lego Principle) Config => A Config => Future[A] Config => Future[Map[String,A]] Config => Future[Map[String,(A,B)]] Config => Future[Map[String,(A,Option[B])]] Markus Hauck (@markus1189) Functional Programming Awesomeness 12 / 41

Slide 24

Slide 24 text

Intro Composition Monoids Errors Conclusion Monoids Compose (Lego Principle) Config => A Config => Future[A] Config => Future[Map[String,A]] Config => Future[Map[String,(A,B)]] Config => Future[Map[String,(A,Option[B])]] Config => Future[Map[String,(Set[A],Option[B])]] Markus Hauck (@markus1189) Functional Programming Awesomeness 12 / 41

Slide 25

Slide 25 text

Intro Composition Monoids Errors Conclusion Lot’s of theory . . . Markus Hauck (@markus1189) Functional Programming Awesomeness 13 / 41

Slide 26

Slide 26 text

Intro Composition Monoids Errors Conclusion • analysis of a potentially huge text • calculate metrics over text • word count • char count • min/max word length • avg word length • . . . (be flexible) • goal: single traversal ↔ easy composition Markus Hauck (@markus1189) Functional Programming Awesomeness 14 / 41

Slide 27

Slide 27 text

Intro Composition Monoids Errors Conclusion RDDs and Folds abstract class RDD[T] { /** * Aggregate the elements of each partition, * and then the results for all the partitions, * using a given associative function and a * neutral "zero value". */ def fold(zeroValue: T)(op: (T, T) => T): T } Markus Hauck (@markus1189) Functional Programming Awesomeness 15 / 41

Slide 28

Slide 28 text

Intro Composition Monoids Errors Conclusion Monoidal RDDs implicit class MonoidRDD[T](val rdd: RDD[T]) { // avoid conflicts with fold/reduce etc def combine(implicit M: Monoid[T]): T = rdd.fold(M.empty)(M.combine(_,_)) } Markus Hauck (@markus1189) Functional Programming Awesomeness 16 / 41

Slide 29

Slide 29 text

Intro Composition Monoids Errors Conclusion The Program val sc: SparkContext = ??? val file: String = ??? val data = sc.textFile(file). // read the file flatMap(_.split("""\W+""")). // split into words map(expand) // action! def expand(w: String) = (1, w.length, Map(w -> 1)) val (words,chars,wordMap) = data.combine Markus Hauck (@markus1189) Functional Programming Awesomeness 17 / 41

Slide 30

Slide 30 text

Intro Composition Monoids Errors Conclusion Running this program Scala Meetup: Rhein-Main Scala Enthusiasts Markus Hauck (@markus1189) Functional Programming Awesomeness 18 / 41

Slide 31

Slide 31 text

Intro Composition Monoids Errors Conclusion Running this program Scala Meetup: Rhein-Main Scala Enthusiasts Seq("Scala","Meetup","Rhein","Main","Scala", ...) Markus Hauck (@markus1189) Functional Programming Awesomeness 18 / 41

Slide 32

Slide 32 text

Intro Composition Monoids Errors Conclusion Running this program Scala Meetup: Rhein-Main Scala Enthusiasts Seq("Scala","Meetup","Rhein","Main","Scala", ...) Seq( // expand(w: String) = (1,w.length,Map(w->1)) (1, 5, Map("Scala" -> 1)), (1, 6, Map("Meetup" -> 1)), (1, 5, Map("Rhein" -> 1)), (1, 4, Map("Main" -> 1)), // ... ) Markus Hauck (@markus1189) Functional Programming Awesomeness 18 / 41

Slide 33

Slide 33 text

Intro Composition Monoids Errors Conclusion Running this program Scala Meetup: Rhein-Main Scala Enthusiasts Seq("Scala","Meetup","Rhein","Main","Scala", ...) Seq( // expand(w: String) = (1,w.length,Map(w->1)) (1, 5, Map("Scala" -> 1)), (1, 6, Map("Meetup" -> 1)), (1, 5, Map("Rhein" -> 1)), (1, 4, Map("Main" -> 1)), // ... ) (6,36,Map("Scala" -> 2,"Meetup" -> 1,...)) Markus Hauck (@markus1189) Functional Programming Awesomeness 18 / 41

Slide 34

Slide 34 text

Intro Composition Monoids Errors Conclusion Easy Extension val data: RDD[String] = ??? // as before def expand(w: String) = ( 1, Max(w.length), // max word length Min(w.length), // min word length Map(w.length -> Set(w)) // words by count ) val (count,max,min,byCount) = data.combine Markus Hauck (@markus1189) Functional Programming Awesomeness 19 / 41

Slide 35

Slide 35 text

Intro Composition Monoids Errors Conclusion The new program Scala Meetup: Rhein-Main Scala Enthusiasts Markus Hauck (@markus1189) Functional Programming Awesomeness 20 / 41

Slide 36

Slide 36 text

Intro Composition Monoids Errors Conclusion The new program Scala Meetup: Rhein-Main Scala Enthusiasts Seq("Scala","Meetup","Rhein","Main","Scala", ...) Markus Hauck (@markus1189) Functional Programming Awesomeness 20 / 41

Slide 37

Slide 37 text

Intro Composition Monoids Errors Conclusion The new program Scala Meetup: Rhein-Main Scala Enthusiasts Seq("Scala","Meetup","Rhein","Main","Scala", ...) Seq( (1, Max(5), Min(5), Map(5 -> Set("Scala"))), (1, Max(6), Min(6), Map(6 -> Set("Meetup"))), (1, Max(5), Min(5), Map(5 -> Set("Rhein"))), (1, Max(4), Min(4), Map(4 -> Set("Main"))), (1, Max(5), Min(5), Map(5 -> Set("Scala"))), // ... ) Markus Hauck (@markus1189) Functional Programming Awesomeness 20 / 41

Slide 38

Slide 38 text

Intro Composition Monoids Errors Conclusion The new program Scala Meetup: Rhein-Main Scala Enthusiasts Seq("Scala","Meetup","Rhein","Main","Scala", ...) Seq( (1, Max(5), Min(5), Map(5 -> Set("Scala"))), (1, Max(6), Min(6), Map(6 -> Set("Meetup"))), (1, Max(5), Min(5), Map(5 -> Set("Rhein"))), (1, Max(4), Min(4), Map(4 -> Set("Main"))), (1, Max(5), Min(5), Map(5 -> Set("Scala"))), // ... ) (6,Max(11),Min(4),Map(5->Set("Scala","Rhein"),...)) Markus Hauck (@markus1189) Functional Programming Awesomeness 20 / 41

Slide 39

Slide 39 text

Intro Composition Monoids Errors Conclusion More Monoid Tricks • “filter” values via mempty value • map + reduce == two phase computation via monoids • finger trees, choose Monoid, get random access list / queue / . . . • Monoids Theme and Variations (Functional Pearl) Markus Hauck (@markus1189) Functional Programming Awesomeness 21 / 41

Slide 40

Slide 40 text

Intro Composition Monoids Errors Conclusion Part Two: Errors Markus Hauck (@markus1189) Functional Programming Awesomeness 22 / 41

Slide 41

Slide 41 text

Intro Composition Monoids Errors Conclusion The Traditional Way • Java style: try/catch/finally • checked exceptions • compiler help • reduce return value checking • unchecked exceptions • only visible via docs, if documented at all • the “way to go” in Java? • errors? return null / custom classes Markus Hauck (@markus1189) Functional Programming Awesomeness 23 / 41

Slide 42

Slide 42 text

Intro Composition Monoids Errors Conclusion The Functional Way • Scala: only unchecked exceptions • but: throw and catch discouraged in FP anyway • FP: type system + first class values = referential transparency! Markus Hauck (@markus1189) Functional Programming Awesomeness 24 / 41

Slide 43

Slide 43 text

Intro Composition Monoids Errors Conclusion Referential Transparency • referential transparency (sounds scary?) • enables you to replace 40 + 2 with 42 • local reasoning & equational reasoning ♥ • parts can be reused independently like lego bricks Markus Hauck (@markus1189) Functional Programming Awesomeness 25 / 41

Slide 44

Slide 44 text

Intro Composition Monoids Errors Conclusion Out Of The Box • Either / Try • Either unbiased (before 2.12) • Try not a lawful monad. . . • get the most by using a FP library Markus Hauck (@markus1189) Functional Programming Awesomeness 26 / 41

Slide 45

Slide 45 text

Intro Composition Monoids Errors Conclusion Try and Catch def convert(is: String*): List[Int] = is.map(_.toInt).toList convert("1","2","3","Hello World!","5") java.lang.NumberFormatException: For input string: "Hello World" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) at java.lang.Integer.parseInt(Integer.java:580) at java.lang.Integer.parseInt(Integer.java:615) ... Markus Hauck (@markus1189) Functional Programming Awesomeness 27 / 41

Slide 46

Slide 46 text

Intro Composition Monoids Errors Conclusion Try and Catch: Not Compositonal try { convert("1","2","3","Hello World!","5") } catch { case e: NumberFormatException => println("Oops") } • type says nothing about errors • only one option: fail fast • what about getting all errors or non-fatal warnings • problem is that try/catch does not compose (Duplo) Markus Hauck (@markus1189) Functional Programming Awesomeness 28 / 41

Slide 47

Slide 47 text

Intro Composition Monoids Errors Conclusion Example Time • password validation • constraints: • length ≥ 8 • contains at least one number • contains no spaces • contains at least one upper char Markus Hauck (@markus1189) Functional Programming Awesomeness 29 / 41

Slide 48

Slide 48 text

Intro Composition Monoids Errors Conclusion Validating Passwords sealed trait LoginError case object PwTooShort extends LoginError case object PwContainsSpace extends LoginError def checkLength(s: String): Option[LoginError] def checkSpace(s: String): Option[LoginError] def convert[E](o: Option[E]): Either[E, Unit] def login(s: String): Either[LoginError,Token] = for { _ <- convert(checkLength(input)) _ <- convert(checkSpace(input)) // ... token = createToken() } yield token Markus Hauck (@markus1189) Functional Programming Awesomeness 30 / 41

Slide 49

Slide 49 text

Intro Composition Monoids Errors Conclusion Using our Function > cabbage Sorry the password must be more than 8 chars > boiled cabbage Sorry, the password must contain at least one digit > 1 boiled cabbage Sorry, the password cannot have spaces > 50damnedboiledcabbages Sorry, the password must contain at least one upper char Markus Hauck (@markus1189) Functional Programming Awesomeness 31 / 41

Slide 50

Slide 50 text

Intro Composition Monoids Errors Conclusion Using our Function Markus Hauck (@markus1189) Functional Programming Awesomeness 32 / 41

Slide 51

Slide 51 text

Intro Composition Monoids Errors Conclusion Validated • scenario: multiple unrelated error conditions, result is fail/success • Either,Xor,etc. are made for fail-fast, or short circuiting • solution: Validated / Validation Markus Hauck (@markus1189) Functional Programming Awesomeness 33 / 41

Slide 52

Slide 52 text

Intro Composition Monoids Errors Conclusion Passwords with Validated sealed trait LoginError case object PwTooShort extends LoginError case object PwContainsSpace extends LoginError case object PwContainsNoDigit extends LoginError def checkLength(s: String): Option[LoginError] def checkSpace(s: String): Option[LoginError] Markus Hauck (@markus1189) Functional Programming Awesomeness 34 / 41

Slide 53

Slide 53 text

Intro Composition Monoids Errors Conclusion Passwords with Validated def convert[E](o:Option[E]):ValidatedNel[E, Unit] def login(s: String): ValidatedNel[LoginError,Token] = { (convert(checkLength(input)) |+| convert(checkSpace(input)) |+| ... ).as(createToken()) } Markus Hauck (@markus1189) Functional Programming Awesomeness 35 / 41

Slide 54

Slide 54 text

Intro Composition Monoids Errors Conclusion Using Validated login("cabbage") Invalid(NonEmptyList(PwTooShort, PwContainsNoDigit, PwContainsNoSpecialChar)) > cabbage Sorry the password is too short (min. 8 chars), contains no digit and no special character Markus Hauck (@markus1189) Functional Programming Awesomeness 36 / 41

Slide 55

Slide 55 text

Intro Composition Monoids Errors Conclusion Using Validated Markus Hauck (@markus1189) Functional Programming Awesomeness 37 / 41

Slide 56

Slide 56 text

Intro Composition Monoids Errors Conclusion Ior (cats) and \&/ (scalaz) • what about “non-fatal” exceptions • instead of short circuiting, continue with warning • there might still be fatal situations with short circuiting • solution: Ior and \&/ Markus Hauck (@markus1189) Functional Programming Awesomeness 38 / 41

Slide 57

Slide 57 text

Intro Composition Monoids Errors Conclusion First Class Errors • replace try / catch with first class values • Either / Xor and friends = fail-fast • Validated /Validation = multiple independent errors • Ior = fail/succeed/succeed with warnings Markus Hauck (@markus1189) Functional Programming Awesomeness 39 / 41

Slide 58

Slide 58 text

Intro Composition Monoids Errors Conclusion • FP has some nice patterns for you • Monoids: combine stuff • Functional Error Handling Markus Hauck (@markus1189) Functional Programming Awesomeness 40 / 41

Slide 59

Slide 59 text

Intro Composition Monoids Errors Conclusion Enough Duplo, time for Lego! . THANKS! Markus Hauck (@markus1189) Functional Programming Awesomeness 41 / 41