Slide 1

Slide 1 text

Intro Composition Monoids Errors Conclusion Applying FP Patterns Markus Hauck (@markus1189) Scala.IO, 2016 Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 1

Slide 2

Slide 2 text

Intro Composition Monoids Errors Conclusion Introduction FP becomes more popular there are many useful patterns Monoids Better Error Handling Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 2

Slide 3

Slide 3 text

Intro Composition Monoids Errors Conclusion Lego vs Duplo pictures from shop.lego.com Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 3

Slide 4

Slide 4 text

Intro Composition Monoids Errors Conclusion Lego vs Duplo pictures from shop.lego.com Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 4

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) - Applying FP Patterns - codecentric AG 5

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) - Applying FP Patterns - codecentric AG 5

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) - Applying FP Patterns - codecentric AG 5

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) - Applying FP Patterns - codecentric AG 6

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) - Applying FP Patterns - codecentric AG 7

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) - Applying FP Patterns - codecentric AG 8

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) - Applying FP Patterns - codecentric AG 9

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) - Applying FP Patterns - codecentric AG 10

Slide 13

Slide 13 text

Intro Composition Monoids Errors Conclusion Monoid Zoo List[A] is a Monoid Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 11

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) - Applying FP Patterns - codecentric AG 11

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) - Applying FP Patterns - codecentric AG 11

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) - Applying FP Patterns - codecentric AG 11

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) - Applying FP Patterns - codecentric AG 11

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) - Applying FP Patterns - codecentric AG 11

Slide 19

Slide 19 text

Intro Composition Monoids Errors Conclusion Monoids Compose (Lego Principle) Config => A Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 12

Slide 20

Slide 20 text

Intro Composition Monoids Errors Conclusion Monoids Compose (Lego Principle) Config => A Config => Future[A] Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 12

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) - Applying FP Patterns - codecentric AG 12

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) - Applying FP Patterns - codecentric AG 12

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) - Applying FP Patterns - codecentric AG 12

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) - Applying FP Patterns - codecentric AG 12

Slide 25

Slide 25 text

Intro Composition Monoids Errors Conclusion I thought this was about applying patterns! Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 13

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) - Applying FP Patterns - codecentric AG 14

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) - Applying FP Patterns - codecentric AG 15

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) - Applying FP Patterns - codecentric AG 16

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) - Applying FP Patterns - codecentric AG 17

Slide 30

Slide 30 text

Intro Composition Monoids Errors Conclusion Running this program Scala.io The Scala event in France Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 18

Slide 31

Slide 31 text

Intro Composition Monoids Errors Conclusion Running this program Scala.io The Scala event in France Seq("Scala","io","The","Scala","event","in",...) Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 18

Slide 32

Slide 32 text

Intro Composition Monoids Errors Conclusion Running this program Scala.io The Scala event in France Seq("Scala","io","The","Scala","event","in",...) Seq( // expand(w: String) = (1,w.length,Map(w->1)) (1, 5, Map("Scala" -> 1)), (1, 2, Map("io" -> 1)), (1, 3, Map("The" -> 1)), (1, 5, Map("Scala" -> 1)), // ... ) Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 18

Slide 33

Slide 33 text

Intro Composition Monoids Errors Conclusion Running this program Scala.io The Scala event in France Seq("Scala","io","The","Scala","event","in",...) Seq( // expand(w: String) = (1,w.length,Map(w->1)) (1, 5, Map("Scala" -> 1)), (1, 2, Map("io" -> 1)), (1, 3, Map("The" -> 1)), (1, 5, Map("Scala" -> 1)), // ... ) (7,28,Map("Scala" -> 2,"io" -> 1,"The" -> 1,...)) Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 18

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) - Applying FP Patterns - codecentric AG 19

Slide 35

Slide 35 text

Intro Composition Monoids Errors Conclusion The new program Scala.io The Scala event in France Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 20

Slide 36

Slide 36 text

Intro Composition Monoids Errors Conclusion The new program Scala.io The Scala event in France Seq("Scala","io","The","Scala","event","in",...) Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 20

Slide 37

Slide 37 text

Intro Composition Monoids Errors Conclusion The new program Scala.io The Scala event in France Seq("Scala","io","The","Scala","event","in",...) Seq( (1, Max(5), Min(5), Map(5 -> Set("Scala"))), (1, Max(2), Min(2), Map(2 -> Set("io"))), (1, Max(3), Min(3), Map(3 -> Set("The"))), (1, Max(5), Min(5), Map(5 -> Set("Scala"))), (1, Max(5), Min(5), Map(5 -> Set("event"))), // ... ) Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 20

Slide 38

Slide 38 text

Intro Composition Monoids Errors Conclusion The new program Scala.io The Scala event in France Seq("Scala","io","The","Scala","event","in",...) Seq( (1, Max(5), Min(5), Map(5 -> Set("Scala"))), (1, Max(2), Min(2), Map(2 -> Set("io"))), (1, Max(3), Min(3), Map(3 -> Set("The"))), (1, Max(5), Min(5), Map(5 -> Set("Scala"))), (1, Max(5), Min(5), Map(5 -> Set("event"))), // ... ) (7,Max(6),Min(2),Map(5->Set("Scala","event"),...)) Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 20

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) - Applying FP Patterns - codecentric AG 21

Slide 40

Slide 40 text

Intro Composition Monoids Errors Conclusion Part Two: Errors Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 22

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 currently the only way to go in Java errors? return null / custom classes Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 23

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 Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 24

Slide 43

Slide 43 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) - Applying FP Patterns - codecentric AG 25

Slide 44

Slide 44 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) - Applying FP Patterns - codecentric AG 26

Slide 45

Slide 45 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) - Applying FP Patterns - codecentric AG 27

Slide 46

Slide 46 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) - Applying FP Patterns - codecentric AG 28

Slide 47

Slide 47 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]): Xor[E, Unit] def login(s: String): Xor[LoginError,Token] = for { _ <- convert(checkLength(input)) _ <- convert(checkSpace(input)) // ... token = createToken() } yield token Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 29

Slide 48

Slide 48 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) - Applying FP Patterns - codecentric AG 30

Slide 49

Slide 49 text

Intro Composition Monoids Errors Conclusion Using our Function Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 31

Slide 50

Slide 50 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) - Applying FP Patterns - codecentric AG 32

Slide 51

Slide 51 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) - Applying FP Patterns - codecentric AG 33

Slide 52

Slide 52 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) - Applying FP Patterns - codecentric AG 34

Slide 53

Slide 53 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) - Applying FP Patterns - codecentric AG 35

Slide 54

Slide 54 text

Intro Composition Monoids Errors Conclusion Using Validated Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 36

Slide 55

Slide 55 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) - Applying FP Patterns - codecentric AG 37

Slide 56

Slide 56 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) - Applying FP Patterns - codecentric AG 38

Slide 57

Slide 57 text

Intro Composition Monoids Errors Conclusion FP has some nice patterns for you Monoids: combine stuff Functional Error Handling Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 39

Slide 58

Slide 58 text

Intro Composition Monoids Errors Conclusion Enough Duplo, time for Lego! . THANKS! Markus Hauck (@markus1189) - Applying FP Patterns - codecentric AG 40