Slide 1

Slide 1 text

Functional Programming with Effects Rob Norris • Scala Days NYC • 20 June 2018 Gemini Observatory / AURA / Julia I. Arias and Rodolfo H. Barbá Departemanto de Fisica / Universidad de La Serena (Chile) ICATE-CONICET (Argentina)

Slide 2

Slide 2 text

Hello • I'm Rob, I do functional programming. • I live in Portland. • I'm @tpolecat pretty much everywhere, I'm easy to find. • I work on a bunch of open-source FP libraries for Scala. • I'm a programmer at the Gemini Observatory (gemini.edu).

Slide 3

Slide 3 text

Goals for Today • Do not panic. • Understand what FP is, and why it's useful. • Gain some insight into the way functional programmers think about things. • Understand why monads are useful and where they come from. • Be inspired to be curious.

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

Functional Programming

Slide 6

Slide 6 text

Functional Programming

Slide 7

Slide 7 text

Functional Programming A B f

Slide 8

Slide 8 text

Functional Programming A B f

Slide 9

Slide 9 text

Functional Programming A B f

Slide 10

Slide 10 text

Functional Programming • Such functions are said to be pure. • Output is determined entirely by the input. • Consequence of programming with pure functions: • Evaluating an expression always results in the same answer. • We can always inline a function call, or factor one out, because we know we'll always get the same answer. • More generally we can always substitute a variable for the expression it's bound to, or introduce a new variable to factor out common sub-expressions. This property of expressions is called referential transparency. A B f

Slide 11

Slide 11 text

Referential Transparency // program 1 val a = (a, a) // program 2 (, ) Are these programs the same? It depends …

Slide 12

Slide 12 text

Referential Transparency // program 1 val a = 42 (a, a) // program 2 (42, 42) Are these programs the same? Yes!

Slide 13

Slide 13 text

Referential Transparency // program 1 val a = println("hi") (a, a) // program 2 (println("hi"), println("hi")) Are these programs the same? No!

Slide 14

Slide 14 text

Referential Transparency // program 1 val a = iter.next() // an iterator (a, a) // program 2 (iter.next(), iter.next()) Are these programs the same? No!

Slide 15

Slide 15 text

Referential Transparency // program 1 val a = Array(1, 2, 3) (a, a) // program 2 (Array(1, 2, 3), Array(1, 2, 3)) Are these programs the same? It depends …

Slide 16

Slide 16 text

Referential Transparency • Every expression is either referentially transparent, or … • it's not. In which case we call it a side-effect. It's one or the other. • This is a syntactic property of programs.

Slide 17

Slide 17 text

World of Expressions • Functional programs are expressions. • Running a functional program means we're evaluating an expression. • We reason about our programs by substitution. • We build bigger programs out of smaller ones by composing them.

Slide 18

Slide 18 text

Function Composition A B C f g

Slide 19

Slide 19 text

Function Composition A B C f g

Slide 20

Slide 20 text

Function Composition A B C f andThen g

Slide 21

Slide 21 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a))

Slide 22

Slide 22 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) // right association f andThen (g andThen h)

Slide 23

Slide 23 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) // right association a => (g andThen h)(f(a))

Slide 24

Slide 24 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) // right association a => (b => h(g(b)))(f(a))

Slide 25

Slide 25 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) // right association a => h(g(f(a)))

Slide 26

Slide 26 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) // right association a => h(g(f(a))) // left association (f andThen g) andThen h

Slide 27

Slide 27 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) // right association a => h(g(f(a))) // left association a => h((f andThen g)(a))

Slide 28

Slide 28 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) // right association a => h(g(f(a))) // left association a => h((b => g(f(b)))(a))

Slide 29

Slide 29 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) // right association a => h(g(f(a))) // left association a => h(g(f(a)))

Slide 30

Slide 30 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) // a new legal substitution (f andThen g) andThen h = f andThen (g andThen h)

Slide 31

Slide 31 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) def id[A]: A => A = a => a

Slide 32

Slide 32 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) def id[A]: A => A = a => a // right identity f andThen id

Slide 33

Slide 33 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) def id[A]: A => A = a => a // right identity a => id(f(a))

Slide 34

Slide 34 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) def id[A]: A => A = a => a // right identity a => (b => b)(f(a))

Slide 35

Slide 35 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) def id[A]: A => A = a => a // right identity a => f(a)

Slide 36

Slide 36 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) def id[A]: A => A = a => a // right identity f

Slide 37

Slide 37 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) def id[A]: A => A = a => a // right identity f andThen id = f

Slide 38

Slide 38 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) def id[A]: A => A = a => a // right identity f andThen id = f // left identity id andThen f

Slide 39

Slide 39 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) def id[A]: A => A = a => a // right identity f andThen id = f // left identity a => f(id(a))

Slide 40

Slide 40 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) def id[A]: A => A = a => a // right identity f andThen id = f // left identity a => f((b => b)(a))

Slide 41

Slide 41 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) def id[A]: A => A = a => a // right identity f andThen id = f // left identity a => f(a)

Slide 42

Slide 42 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) def id[A]: A => A = a => a // right identity f andThen id = f // left identity f

Slide 43

Slide 43 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) def id[A]: A => A = a => a // right identity f andThen id = f // left identity id andThen f = f

Slide 44

Slide 44 text

Function Composition def andThen[A, B, C](f: A => B, g: B => C): A => C = a => g(f(a)) def id[A]: A => A = a => a // right identity f andThen id = f // left identity id andThen f = f // associativity (f andThen g) andThen h = f andThen (g andThen h)

Slide 45

Slide 45 text

Function Composition A B C f: A => B g: B 㱺C (f andThen g): A => C id[A]: A => A • Our objects are types. • Our arrow are pure functions. • Our associative composition op is andThen. • Our identity arrows at each object are id[A]. Category of Scala Types and Functions

Slide 46

Slide 46 text

So what about … • Partiality? • Exceptions? • Nondeterminism? • Dependency injection? • Logging? • Mutable state? • Imperative programming generally?

Slide 47

Slide 47 text

Credit: Katherine de Kleer and Imke de Pater, UC Berkeley/Gemini Observatory/AURA/W.M. Keck Observatory

Slide 48

Slide 48 text

Six Effects

Slide 49

Slide 49 text

Let's talk about Option // Abbreviated Definition sealed trait Option[+A] case object None extends Option[Nothing] case class Some[A](a: A) extends Option[A] // Functons that may not yield an answer val f: A => Option[B] val g: B => Option[C] // We can't compose them :-( f andThen g // type error

Slide 50

Slide 50 text

Let's talk about Either // Abbreviated Definition sealed trait Either[+A, +B] case class Left [+A, +B](a: A) extends Either[A, B] case class Right[+A, +B](b: A) extends Either[A, B] // Intuition: Functions that may fail with a reason. val f: A => Either[String, B] val g: B => Either[String, C] // We can't compose them :-( f andThen g // type error

Slide 51

Slide 51 text

Let's talk about List // Abbreviated Definition sealed trait List[+A] case object Nil extends List[Nothing] case class ::[A](head: A, tail: List[S]) extends List[A] // Intuition: Functions that may yield many answers val f: A => List[B] val g: B => List[C] // We can't compose them :-( f andThen g // type error

Slide 52

Slide 52 text

Let's talk about Reader // Abbreviated Definition case class Reader[E, A](run: E => A) // Intuition: Computations with dependencies. val f: A => Reader[Config, B] // equivalent to A => (Config => B) val g: B => Reader[Config, C]

Slide 53

Slide 53 text

Let's talk about Reader // Abbreviated Definition case class Reader[E, A](run: E => A) // Example type Host = String def path(s: String): Reader[Host, String] = Reader { host => s"http: //$host/$s" } val p = path("foo/bar") p.run("google.com") // http: //google.com/foo/bar p.run("tpolecat.org") // http: //tpolecat.org/foo/bar

Slide 54

Slide 54 text

Let's talk about Reader // Abbreviated Definition case class Reader[E, A](run: E => A) // Intuition: Computatons with dependencies. val f: A => Reader[Config, B] // equivalent to A => (Config => B) val g: B => Reader[Config, C]

Slide 55

Slide 55 text

Let's talk about Reader // Abbreviated Definition case class Reader[E, A](run: E => A) // Intuition: Computations with dependencies. val f: A => Reader[Config, B] // equivalent to A => (Config => B) val g: B => Reader[Config, C] // We can't compose them :-( f andThen g // type error

Slide 56

Slide 56 text

Let's talk about Writer // Abbreviated Definition case class Writer[W, A](w: W, a: A) // Intuition: Functions that annotate the values they compute. val f: A => Writer[Info, B] // equivalent to A => (Info, B) val g: B => Writer[Info, C]

Slide 57

Slide 57 text

Let's talk about Writer // Abbreviated Definition case class Writer[W, A](w: W, a: A) // Example type Log = List[String] def toDouble(n: Int): Writer[Log, Double] = Writer(List(s"Converted $n to Double!"), n.toDouble) toDouble(10) // Writer(List(Converted 10 to Double!),10.0)

Slide 58

Slide 58 text

Let's talk about Writer // Abbreviated Definition case class Writer[W, A](w: W, a: A) // Intuition: Functions that annotate the values they compute. val f: A => Writer[Info, B] // equivalent to A => (Info, B) val g: B => Writer[Info, C] // We can't compose them :-( f andThen g // type error

Slide 59

Slide 59 text

Let's talk about State // Abbreviated Definition case class State[S, A](run: S => (A, S)) // Intuition: Computations with a state transition. val f: A => State[Info, B] // equivalent to A => (B => (B, Info)) val g: B => State[Info, C]

Slide 60

Slide 60 text

Let's talk about State // Abbreviated Definition case class State[S, A](run: S => (A, S)) // Example type Counter = Int def greet(name: String): State[Counter, String] = State { count => (s"Hello $name, you are person number $count", count + 1) } val x = greet("Bob") x.run(1) // (Hello Bob, you are person number 1,2) x.run(20) // (Hello Bob, you are person number 20,21)

Slide 61

Slide 61 text

Let's talk about State // Abbreviated Definition case class State[S, A](run: S => (A, S)) // Intuition: Computations with a state transition. val f: A => State[Info, B] // equivalent to A => (B => (B, Info)) val g: B => State[Info, C] // We can't compose them :-( f andThen g // type error

Slide 62

Slide 62 text

What do they have in common? • All compute an "answer"but also encapsulate something extra about the computation. • This is what we call an effect. But it's very vague. Can we be more precise about what they have in common?

Slide 63

Slide 63 text

All have shape F[A] type F[A] = Option[A] type F[A] = Either[E, A] // for some type E type F[A] = List[A] type F[A] = Reader[E, A] // for some type E type F[A] = Writer[W, A] // for some type W type F[A] = State[S, A] // for some type S An effect is whatever distinguishes F[A] from A .

Slide 64

Slide 64 text

All have shape F[A] F[A] "This is a program in F that computes a value of type A." The Effect

Slide 65

Slide 65 text

But they don't compose! scala> val char10: String => Option[Char] = | s => s.lift(10) char10: String => Option[Char] = $$Lambda$5661/390122011@37974d1f scala> val letter: Char => Option[Int] = | c => if (c.isLetter) Some(c.toInt) else None letter: Char => Option[Int] = $$Lambda$5662/973361211@77d94464 scala> char10 andThen letter :16: error: type mismatch; found : Char => Option[Int] required: Option[Char] => ? char10 andThen letter ^

Slide 66

Slide 66 text

Credit: Gemini Observatory/NSF/NASA/AURA

Slide 67

Slide 67 text

The Problem

Slide 68

Slide 68 text

Function Composition A B C f: A => B g: B 㱺C (f andThen g): A => C id[A]: A => A

Slide 69

Slide 69 text

Function Composition A B C f: A => F[B] g: B 㱺F[C] (f andThen g): A => F[C] id[A]: A => F[A]

Slide 70

Slide 70 text

Function Composition A B C f: A => F[B] g: B 㱺F[C] (f >=> g): A => F[C] pure[A]: A => F[A]

Slide 71

Slide 71 text

The Operations // A typeclass that describes type constructors that allow composition with >=> trait Fishy[F[_]] { // Our identity, A => F[A] for any type A def pure[A](a: A): F[A] // Composition - the "fish" operator def >=>[A, B, C](f: A => F[B], g: B => F[C]): A => F[C] }

Slide 72

Slide 72 text

The Operations // A typeclass that describes type constructors that allow composition with >=> trait Fishy[F[_]] { // Our identity, A => F[A] for any type A def pure[A](a: A): F[A] // Composition - the "fish" operator def >=>[A, B, C](f: A => F[B], g: B => F[C]): A => F[C] = a => f(a) // we have an F[B] and a B => F[C] and we want an F[C] }

Slide 73

Slide 73 text

The Operations // A typeclass that describes type constructors that allow composition with >=> trait Fishy[F[_]] { // Our identity, A => F[A] for any type A def pure[A](a: A): F[A] // Composition - the "fish" operator def >=>[A, B, C](f: A => F[B], g: B => F[C]): A => F[C] = a => f(a).flatMap(g) // hey that looks like flatMap! }

Slide 74

Slide 74 text

The Operations // A typeclass that describes type constructors that allow composition with >=> trait Fishy[F[_]] { // Our identity, A => F[A] for any type A def pure[A](a: A): F[A] // The operation we need if we want to define >=> def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] }

Slide 75

Slide 75 text

The Operations // Now we can define >=> as an infix operator using a syntax class implicit class FishyFunctionOps[F[_], A, B](f: A => F[B]) { def >=>[C](g: B => F[C])(implicit ev: Fishy[F]): A => F[C] = a => ev.flatMap(f(a))(g) }

Slide 76

Slide 76 text

The Operations // Now we can define >=> as an infix operator using a syntax class implicit class FishyFunctionOps[F[_], A, B](f: A => F[B]) { def >=>[C](g: B => F[C])(implicit ev: Fishy[F]): A => F[C] = a => ev.flatMap(f(a))(g) } // Let's define an instance for Option implicit val FishyOption: Fishy[Option] = new Fishy[Option] { def pure[A](a: A) = Some(a) def flatMap[A, B](fa: Option[A])(f: A => Option[B]) = fa.flatMap(f) }

Slide 77

Slide 77 text

The Operations scala> val char10: String => Option[Char] = | s => s.lift(10) char10: String => Option[Char] = $$Lambda$5661/390122011@37974d1f scala> val letter: Char => Option[Int] = | c => if (c.isLetter) Some(c.toInt) else None letter: Char => Option[Int] = $$Lambda$5662/973361211@77d94464 scala> char10 andThen letter :16: error: type mismatch; found : Char => Option[Int] required: Option[Char] => ? char10 andThen letter ^

Slide 78

Slide 78 text

The Operations scala> val char10: String => Option[Char] = | s => s.lift(10) char10: String => Option[Char] = $$Lambda$5661/390122011@37974d1f scala> val letter: Char => Option[Int] = | c => if (c.isLetter) Some(c.toInt) else None letter: Char => Option[Int] = $$Lambda$5662/973361211@77d94464 scala> char10 >=> letter res5: String => Option[Int] = FishyFunctionOps$$Lambda$5664/537915194@1c9443ec

Slide 79

Slide 79 text

The Operations scala> char10 >=> letter res5: String => Option[Int] = FishyFunctionOps$$Lambda$5664/537915194@1c9443ec

Slide 80

Slide 80 text

The Operations scala> char10 >=> letter res5: String => Option[Int] = FishyFunctionOps$$Lambda$5664/537915194@1c9443ec scala> res5("foo") res6: Option[Int] = None scala> res5("foobarbazqux") res7: Option[Int] = Some(117) scala> res5("foobarbazq9x") res8: Option[Int] = None

Slide 81

Slide 81 text

The Rules A B C f: A => F[B] g: B 㱺'=> g): A => F[C] pure[A]: A => F[A]

Slide 82

Slide 82 text

The Rules A B C f: A => F[B] g: B 㱺'=> g): A => F[C] pure[A]: A => F[A] // left identity pure >=> f ≡ f // right identity f >=> pure ≡ f // associativity f >=> (g >=> h) ≡ (f >=> g) >=> h

Slide 83

Slide 83 text

The Rules // left identity pure >=> f ≡ f

Slide 84

Slide 84 text

The Rules // left identity a => pure(a).flatMap(f) ≡ f

Slide 85

Slide 85 text

The Rules // left identity a => pure(a).flatMap(f) ≡ a => f(a)

Slide 86

Slide 86 text

The Rules // left identity pure(a).flatMap(f) ≡ f(a)

Slide 87

Slide 87 text

The Rules // left identity pure(a).flatMap(f) ≡ f(a) // right identity f >=> pure ≡ f

Slide 88

Slide 88 text

The Rules // left identity pure(a).flatMap(f) ≡ f(a) // right identity a => f(a).flatMap(pure) ≡ f

Slide 89

Slide 89 text

The Rules // left identity pure(a).flatMap(f) ≡ f(a) // right identity a => f(a).flatMap(pure) ≡ a => f(a)

Slide 90

Slide 90 text

The Rules // left identity pure(a).flatMap(f) ≡ f(a) // right identity f(a).flatMap(pure) ≡ f(a)

Slide 91

Slide 91 text

The Rules // left identity pure(a).flatMap(f) ≡ f(a) // right identity m.flatMap(pure) ≡ m

Slide 92

Slide 92 text

The Rules // left identity pure(a).flatMap(f) ≡ f(a) // right identity m.flatMap(pure) ≡ m // left-associative composition (f >=> g) >=> h

Slide 93

Slide 93 text

The Rules // left identity pure(a).flatMap(f) ≡ f(a) // right identity m.flatMap(pure) ≡ m // left-associative composition a => (f >=> g)(a).flatMap(h)

Slide 94

Slide 94 text

The Rules // left identity pure(a).flatMap(f) ≡ f(a) // right identity m.flatMap(pure) ≡ m // left-associative composition a => (b => f(b).flatMap(g))(a).flatMap(h)

Slide 95

Slide 95 text

The Rules // left identity pure(a).flatMap(f) ≡ f(a) // right identity m.flatMap(pure) ≡ m // left-associative composition a => f(a).flatMap(g).flatMap(h)

Slide 96

Slide 96 text

The Rules // left identity pure(a).flatMap(f) ≡ f(a) // right identity m.flatMap(pure) ≡ m // left-associative composition a => f(a).flatMap(g).flatMap(h) // right-associative composition f >=> (g >=> h) ≡ ≡

Slide 97

Slide 97 text

The Rules // left identity pure(a).flatMap(f) ≡ f(a) // right identity m.flatMap(pure) ≡ m // left-associative composition a => f(a).flatMap(g).flatMap(h) // right-associative composition a => f(a).flatMap(g >=> h) ≡ ≡

Slide 98

Slide 98 text

The Rules // left identity pure(a).flatMap(f) ≡ f(a) // right identity m.flatMap(pure) ≡ m // left-associative composition a => f(a).flatMap(g).flatMap(h) // right-associative composition a => f(a).flatMap(b => g(b).flatMap(h)) ≡ ≡

Slide 99

Slide 99 text

The Rules // left identity pure(a).flatMap(f) ≡ f(a) // right identity m.flatMap(pure) ≡ m // left-associative composition f(a).flatMap(g).flatMap(h) // right-associative composition f(a).flatMap(b => g(b).flatMap(h)) ≡ ≡

Slide 100

Slide 100 text

The Rules // left identity pure(a).flatMap(f) ≡ f(a) // right identity m.flatMap(pure) ≡ m // left-associative composition m.flatMap(g).flatMap(h) // right-associative composition m.flatMap(b => g(b).flatMap(h)) ≡ ≡

Slide 101

Slide 101 text

The Rules // left identity pure(a).flatMap(f) ≡ f(a) // right identity m.flatMap(pure) ≡ m // associativity m.flatMap(g).flatMap(h) ≡ m.flatMap(b => g(b).flatMap(h))

Slide 102

Slide 102 text

Kleisli Category for F A B C f: A => F[B] g: B 㱺'=> g): A => F[C] pure[A]: A => F[A]

Slide 103

Slide 103 text

Fishy // Fishy typeclass trait Fishy[F[_]] { def pure[A](a: A): F[A] def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] }

Slide 104

Slide 104 text

Monad // Monad typeclass trait Monad[F[_]] { def pure[A](a: A): F[A] def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] }

Slide 105

Slide 105 text

Monad // Monad typeclass trait Monad[F[_]] { def pure[A](a: A): F[A] def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] } // Monad laws pure(a).flatMap(f) ≡ f(a) m.flatMap(pure) ≡ m m.flatMap(g).flatMap(h) ≡ m.flatMap(b => g(b).flatMap(h))

Slide 106

Slide 106 text

Monad // Monad typeclass trait Monad[F[_]] { def pure[A](a: A): F[A] def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] }

Slide 107

Slide 107 text

Monad // Monad typeclass trait Monad[F[_]] { def pure[A](a: A): F[A] def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] def map[A, B](fa: F[A])(f: A => B): F[B] }

Slide 108

Slide 108 text

Monad // Monad typeclass trait Monad[F[_]] extends Functor[F] { def pure[A](a: A): F[A] def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] } // Functor typeclass trait Functor[F[_]] { def map[A, B](fa: F[A])(f: A => B): F[B] }

Slide 109

Slide 109 text

Monad // Monad typeclass trait Monad[F[_]] extends Functor[F] { def pure[A](a: A): F[A] def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] def map[A, B](fa: F[A])(f: A => B): F[B] = flatMap(fa)(a => pure(f(a))) }

Slide 110

Slide 110 text

Functor Laws // identity a.map(identity) ≡ a // composition a.map(f).map(g) ≡ a.map(f andThen g)

Slide 111

Slide 111 text

Functor Laws // identity a.map(identity)

Slide 112

Slide 112 text

Functor Laws // identity a.map(identity) a.flatMap(a => pure(identity(a)))) // inline `map`

Slide 113

Slide 113 text

Functor Laws // identity a.map(identity) a.flatMap(a => pure(identity(a)))) // inline `map` a.flatMap(a => pure((x => x)(a)))) // inline `identity`

Slide 114

Slide 114 text

Functor Laws // identity a.map(identity) a.flatMap(a => pure(identity(a)))) // inline `map` a.flatMap(a => pure((x => x)(a)))) // inline `identity` a.flatMap(a => pure(a)) // apply `a`

Slide 115

Slide 115 text

Functor Laws // identity a.map(identity) a.flatMap(a => pure(identity(a)))) // inline `map` a.flatMap(a => pure((x => x)(a)))) // inline `identity` a.flatMap(a => pure(a)) // apply `a` a.flatMap(pure) // eta-reduce

Slide 116

Slide 116 text

Functor Laws // identity a.map(identity) a.flatMap(a => pure(identity(a)))) // inline `map` a.flatMap(a => pure((x => x)(a)))) // inline `identity` a.flatMap(a => pure(a)) // apply `a` a.flatMap(pure) // eta-reduce a // right identity law

Slide 117

Slide 117 text

Functor Laws // composition a.map(f).map(g) ≡ a.map(f andThen g)

Slide 118

Slide 118 text

Functor Laws // composition a.map(f).map(g) ≡ a.map(f andThen g) // composition (1) a.map(f).map(g)

Slide 119

Slide 119 text

Functor Laws // composition a.map(f).map(g) ≡ a.map(f andThen g) // composition (1) a.map(f).map(g) a.flatMap(a => pure(f(a))).flatMap(a => pure(g(a))) // inline map

Slide 120

Slide 120 text

Functor Laws // composition a.map(f).map(g) ≡ a.map(f andThen g) // composition (1) a.map(f).map(g) a.flatMap(a => pure(f(a))).flatMap(a => pure(g(a))) // inline map a.flatMap(a => pure(f(a)).flatMap(a => pure(g(a)))) // associativity

Slide 121

Slide 121 text

Functor Laws // composition a.map(f).map(g) ≡ a.map(f andThen g) // composition (1) a.map(f).map(g) a.flatMap(a => pure(f(a))).flatMap(a => pure(g(a))) // inline map a.flatMap(a => pure(f(a)).flatMap(a => pure(g(a)))) // associativity // left identity pure(a).flatMap(f) ≡ f(a)

Slide 122

Slide 122 text

Functor Laws // composition a.map(f).map(g) ≡ a.map(f andThen g) // composition (1) a.map(f).map(g) a.flatMap(a => pure(f(a))).flatMap(a => pure(g(a))) // inline map a.flatMap(a => pure(f(a)).flatMap(a => pure(g(a)))) // associativity a.flatMap(a => pure(g(f(a)))) // left identity

Slide 123

Slide 123 text

Functor Laws // composition a.map(f).map(g) ≡ a.map(f andThen g) // composition (1) a.map(f).map(g) a.flatMap(a => pure(f(a))).flatMap(a => pure(g(a))) // inline map a.flatMap(a => pure(f(a)).flatMap(a => pure(g(a)))) // associativity a.flatMap(a => pure(g(f(a)))) // left identity // composition (2) a.map(f andThen h)

Slide 124

Slide 124 text

Functor Laws // composition a.map(f).map(g) ≡ a.map(f andThen g) // composition (1) a.map(f).map(g) a.flatMap(a => pure(f(a))).flatMap(a => pure(g(a))) // inline map a.flatMap(a => pure(f(a)).flatMap(a => pure(g(a)))) // associativity a.flatMap(a => pure(g(f(a)))) // left identity // composition (2) a.map(f andThen h) a.flatMap(a => pure((f andThen g)(a)) // inline map

Slide 125

Slide 125 text

Functor Laws // composition a.map(f).map(g) ≡ a.map(f andThen g) // composition (1) a.map(f).map(g) a.flatMap(a => pure(f(a))).flatMap(a => pure(g(a))) // inline map a.flatMap(a => pure(f(a)).flatMap(a => pure(g(a)))) // associativity a.flatMap(a => pure(g(f(a)))) // left identity // composition (2) a.map(f andThen h) a.flatMap(a => pure((f andThen g)(a)) // inline map a.flatMap(a => pure(g(f(a)))) // inline andThen

Slide 126

Slide 126 text

Functor Laws // composition a.map(f).map(g) ≡ a.map(f andThen g) // composition (1) a.map(f).map(g) a.flatMap(a => pure(f(a))).flatMap(a => pure(g(a))) // inline map a.flatMap(a => pure(f(a)).flatMap(a => pure(g(a)))) // associativity a.flatMap(a => pure(g(f(a)))) // left identity // composition (2) a.map(f andThen h) a.flatMap(a => pure((f andThen g)(a)) // inline map a.flatMap(a => pure(g(f(a)))) // inline andThen

Slide 127

Slide 127 text

Functor Laws // composition a.map(f).map(g) ≡ a.map(f andThen g) // composition (1) a.map(f).map(g) a.flatMap(a => pure(f(a))).flatMap(a => pure(g(a))) // inline map a.flatMap(a => pure(f(a)).flatMap(a => pure(g(a)))) // associativity a.flatMap(a => pure(g(f(a)))) // left identity // composition (2) a.map(f andThen h) a.flatMap(a => pure((f andThen g)(a)) // inline map a.flatMap(a => pure(g(f(a)))) // inline andThen ≡

Slide 128

Slide 128 text

Monad // Monad typeclass trait Monad[F[_]] { def pure[A](a: A): F[A] def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] def map[A, B](fa: F[A])(f: A => B): F[B] = flatMap(fa)(a => pure(f(a))) }

Slide 129

Slide 129 text

Monad // Monad typeclass trait Monad[F[_]] { def pure[A](a: A): F[A] def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] def map[A, B](fa: F[A])(f: A => B): F[B] = flatMap(fa)(a => pure(f(a))) def tuple[A, B](fa: F[A], fb: F[B]): F[(A, B)] = flatMap(fa)(a => map(fb)(b => (a, b))) }

Slide 130

Slide 130 text

Monad // Monad syntax implicit class MonadOps[F[_], A](fa: F[A])(implicit ev: Monad[F]) { // Delegate to `ev` def flatMap[B](f: A => F[B]): F[B] = ev.flatMap(fa)(f) def map[B](f: A => B): F[B] = ev.map(fa)(f) def tuple[B](fb: F[B]): F[(A, B)] = ev.tuple(fa, fb) }

Slide 131

Slide 131 text

Monad // Monad syntax implicit class MonadOps[F[_], A](fa: F[A])(implicit ev: Monad[F]) { // Delegate to `ev` def flatMap[B](f: A => F[B]): F[B] = ev.flatMap(fa)(f) def map[B](f: A => B): F[B] = ev.map(fa)(f) def tuple[B](fb: F[B]): F[(A, B)] = ev.tuple(fa, fb) // Derived syntax def <*[B](fb: F[B]): F[A] = ev.map(tuple(fb))(_._1) def *>[B](fb: F[B]): F[B] = ev.map(tuple(fb))(_._2) }

Slide 132

Slide 132 text

Image credit: Marshall Perrin (Space Telescope Science Institute), Gaspard Duchene (UC Berkeley), Max Millar-Blanchaer (University of Toronto), and the GPI Team.

Slide 133

Slide 133 text

Our effects, again.

Slide 134

Slide 134 text

Let's talk about Option Again // Abbreviated Definition sealed trait Option[+A] case object None extends Option[Nothing] case class Some[+A](a: A) extends Option[A]

Slide 135

Slide 135 text

Let's talk about Option Again // Abbreviated Definition sealed trait Option[+A] case object None extends Option[Nothing] case class Some[+A](a: A) extends Option[A] // Monad instance implicit val OptionMonad: Monad[Option] = new Monad[Option] { def pure[A](a: A) = Some(a) def flatMap[A, B](fa: Option[A])(f: A => Option[B]) = fa match { case Some(a) => f(a) case None => None } }

Slide 136

Slide 136 text

Let's talk about Option Again def validate(s: String) = if (s.nonEmpty) Some(s) else None scala> validate("Bob") tuple validate("Dole") res13: Option[(String, String)] = Some((Bob,Dole)) scala> validate("") tuple validate("Dole") res14: Option[(Nothing, String)] = None scala> validate("Bob") *> validate("Dole") res15: Option[String] = Some(Dole) scala> validate("") *> validate("Dole") res16: Option[String] = None scala> validate("Dole") <* validate("Bob") res17: Option[String] = Some(Dole)

Slide 137

Slide 137 text

Let's talk about Either Again // Abbreviated Definition sealed trait Either[+A, +B] case class Left [+A, +B](a: A) extends Either[A, B] case class Right[+A, +B](b: A) extends Either[A, B]

Slide 138

Slide 138 text

Let's talk about Either Again // Abbreviated Definition sealed trait Either[+A, +B] case class Left [+A, +B](a: A) extends Either[A, B] case class Right[+A, +B](b: A) extends Either[A, B] // Monad instance implicit def eitherMonad[L]: Monad[Either[L, ?]] = new Monad[Either[L, ?]] { def pure[A](a: A) = Right(a) def flatMap[A, B](fa: Either[L, A])(f: A => Either[L, B]) = fa match { case Left(l) => Left(l) case Right(a) => f(a) } }

Slide 139

Slide 139 text

Let's talk about Either Again def validate(tag: String, value: String) = if (value.nonEmpty) Right(value) else Left(s"$tag is empty") def validateName(first: String, last: String) = for { first <- validate("First name", first) last <- validate("Last name", last) } yield s"$first $last" scala> validateName("Bob", "Dole") res24: Either[String,String] = Right(Bob Dole) scala> validateName("Bob", "") res25: Either[String,String] = Left(Last name is empty)

Slide 140

Slide 140 text

Let's talk about List Again // Abbreviated Definition sealed trait List[+A] case object Nil extends List[Nothing] case class ::[A](head: A, tail: List[S]) extends List[A]

Slide 141

Slide 141 text

Let's talk about List Again // Abbreviated Definition sealed trait List[+A] case object Nil extends List[Nothing] case class ::[A](head: A, tail: List[S]) extends List[A] // Monad instance implicit val ListMonad: Monad[List] = new Monad[List] { def pure[A](a: A) = a :: Nil def flatMap[A, B](fa: List[A])(f: A => List[B]): List[B] = fa.foldRight(List.empty[B])((a, bs) => f(a) ::: bs) }

Slide 142

Slide 142 text

Let's talk about List Again scala> List(1,2,3) tuple List('x', 'y') res28: List[(Int, Char)] = List((1,x), (1,y), (2,x), (2,y), (3,x), (3,y)) scala> List(1,2,3) *> List('x', 'y') res29: List[Char] = List(x, y, x, y, x, y) scala> List('x', 'y') <* List(1,2,3) res30: List[Char] = List(x, x, x, y, y, y)

Slide 143

Slide 143 text

Let's talk about Reader Again // Abbreviated Definition case class Reader[A, B](run: A => B)

Slide 144

Slide 144 text

Let's talk about Reader Again // Abbreviated Definition case class Reader[A, B](run: A => B) // Monad instance implicit def readerMonad[E]: Monad[Reader[E, ?]] = new Monad[Reader[E, ?]] { def pure[A](a: A) = Reader(e => a) def flatMap[A, B](fa: Reader[E, A])(f: A => Reader[E, B]) = Reader { e => val a = fa.run(e) f(a).run(e) } }

Slide 145

Slide 145 text

Let's talk about Reader Again type Host = String def path(s: String): Reader[Host, String] = Reader(host => s"http: //$host/$s") val hostLen: Reader[Host, Int] = Reader(host => host.length) val prog = for { a <- path("foo/bar") b <- hostLen } yield s"Path is $a and len is $b." scala> prog.run("google.com") res70: String = Path is http: //google.com/foo/bar and len is 10.

Slide 146

Slide 146 text

Let's talk about Writer Again (let's not)

Slide 147

Slide 147 text

Let's talk about State Again // Abbreviated Definition case class State[S, A](run: S => (A, S))

Slide 148

Slide 148 text

Let's talk about State Again // Abbreviated Definition case class State[S, A](run: S => (A, S)) // Monad instance implicit def monadState[S]: Monad[State[S, ?]] = new Monad[State[S, ?]] { def pure[A](a: A) = State(s => (a, s)) def flatMap[A, B](fa: State[S, A])(f: A => State[S, B]) = State { s => val (a, sʹ) = fa.run(s) f(a).run(sʹ) } }

Slide 149

Slide 149 text

Let's talk about State Again type Seed = Int val rnd: State[Seed, Int] = State { s => val next = ((s.toLong * 16807) % Int.MaxValue).toInt (next, next) } val d6 = rnd.map(_ % 6) // 2d6+2 val damage = for { a <- d6 b <- d6 } yield a + b + 2

Slide 150

Slide 150 text

Let's talk about State Again scala> damage.run(17) res60: (Int, Seed) = (10,507111939) scala> for { a <- damage; b <- damage } yield s"damages: $a, $b" res61: State[Seed,String] = State($anon$1$$Lambd ... scala> res61.run(17)._1 res62: String = damages: 10, 6 scala> (damage tuple damage).run(7) res63: ((Int, Int), Seed) = ((4,7),452154665)

Slide 151

Slide 151 text

Credit: Gemini Observatory/AURA/Andrew Levan (University of Warwick, UK)

Slide 152

Slide 152 text

Is that it?

Slide 153

Slide 153 text

pure zip map flatMap The Big Picture

Slide 154

Slide 154 text

Applicative Apply Functor Monad The Big Picture

Slide 155

Slide 155 text

Alternative Applicative ApplicativeError Apply Bimonad Semigroupal CoflatMap Comonad ContravariantSemigroupal FlatMap Foldable Functor InvariantSemigroupal Monad MonadError MonoidK Reducible SemigroupK Contravariant Invariant Traverse NonEmptyTraverse CommutativeFlatMap CommutativeMonad CommutativeApply CommutativeApplicative ContravariantMonoidal Distributive InvariantMonoidal UnorderedFoldable UnorderedTraverse The Big Picture

Slide 156

Slide 156 text

` • Functional Programming • Referential Transparency • Six Effect Types • Monads (+ Functors) • Applicative and Traversable Functors • Composing Effects • A lifetime's worth of other fun stuff. Covered Next Steps

Slide 157

Slide 157 text

Thanks! • Cats
 - typelevel.org/cats
 - gitter.im/typelevel/cats
 - github.com/tpolecat/cats-infographic • Scala with Cats 
 by Noel Welsh and Dave Gurnell
 at underscore.io/training • Functional Programming in Scala
 by Rúnar Bjarnason and Paul Chiusano @tpolecat