Slide 1

Slide 1 text

The Functor, Applicative, Monad talk Adelbert Chang @adelbertchang Scale by the Bay 2017

Slide 2

Slide 2 text

Assumptions We are interested in pure functional programming

Slide 3

Slide 3 text

Assumptions We are interested in pure functional programming For all f : A → B and a : A, there exists a b : B such that f (a) = b

Slide 4

Slide 4 text

Assumptions We are interested in pure functional programming For all f : A → B and a : A, there exists a b : B such that f (a) = b For all expressions f (x) we can safely replace

Slide 5

Slide 5 text

Assumptions We are interested in pure functional programming For all f : A → B and a : A, there exists a b : B such that f (a) = b For all expressions f (x) we can safely replace

Slide 6

Slide 6 text

Assumptions We are interested in pure functional programming For all f : A → B and a : A, there exists a b : B such that f (a) = b For all expressions f (x) we can safely replace g(f(x), f(x))

Slide 7

Slide 7 text

Assumptions We are interested in pure functional programming For all f : A → B and a : A, there exists a b : B such that f (a) = b For all expressions f (x) we can safely replace g(f(x), f(x)) val e = f(x) g(e, e)

Slide 8

Slide 8 text

Implications No null or exception throwing

Slide 9

Slide 9 text

Implications No null or exception throwing No mutation, print statements, or side effects in general

Slide 10

Slide 10 text

Implications No null or exception throwing No mutation, print statements, or side effects in general

Slide 11

Slide 11 text

Implications No null or exception throwing No mutation, print statements, or side effects in general var x = 0 x += 1 x += 1

Slide 12

Slide 12 text

Implications No null or exception throwing No mutation, print statements, or side effects in general var x = 0 x += 1 x += 1 var x = 0 val e = x += 1 e e

Slide 13

Slide 13 text

Getting back what we lost: null

Slide 14

Slide 14 text

Getting back what we lost: null The concept of null is useful, an absence of a value

Slide 15

Slide 15 text

Getting back what we lost: null The concept of null is useful, an absence of a value Instead of having an universal sentinel value that inhabits every type, make it data just like everything else

Slide 16

Slide 16 text

Getting back what we lost: null The concept of null is useful, an absence of a value Instead of having an universal sentinel value that inhabits every type, make it data just like everything else

Slide 17

Slide 17 text

Getting back what we lost: null The concept of null is useful, an absence of a value Instead of having an universal sentinel value that inhabits every type, make it data just like everything else sealed trait Option[A] final case class Some[A](value: A) extends Option[A] final case class None[A]() extends Option[A]

Slide 18

Slide 18 text

Getting back what we lost: throwing exceptions Some functions we want to write will be partial in nature

Slide 19

Slide 19 text

Getting back what we lost: throwing exceptions Some functions we want to write will be partial in nature Instead of giving A give Either[Error, A]

Slide 20

Slide 20 text

Getting back what we lost: throwing exceptions Some functions we want to write will be partial in nature Instead of giving A give Either[Error, A]

Slide 21

Slide 21 text

Getting back what we lost: throwing exceptions Some functions we want to write will be partial in nature Instead of giving A give Either[Error, A] sealed trait Either[E, A] final case class Left[E, A] (e: E) extends Either[E, A] final case class Right[E, A](a: A) extends Either[E, A]

Slide 22

Slide 22 text

Getting back what we lost: mutation Many computations need state they can read and write to

Slide 23

Slide 23 text

Getting back what we lost: mutation Many computations need state they can read and write to View the computation as a function from start state to the computed value and the new state

Slide 24

Slide 24 text

Getting back what we lost: mutation Many computations need state they can read and write to View the computation as a function from start state to the computed value and the new state

Slide 25

Slide 25 text

Getting back what we lost: mutation Many computations need state they can read and write to View the computation as a function from start state to the computed value and the new state final case class State[S, A](run: S => (A, S))

Slide 26

Slide 26 text

A pattern emerges For functions f : A → B that “do something” besides returning a B, augment the return type

Slide 27

Slide 27 text

A pattern emerges For functions f : A → B that “do something” besides returning a B, augment the return type

Slide 28

Slide 28 text

A pattern emerges For functions f : A → B that “do something” besides returning a B, augment the return type def foo(a: Foo): Option[Bar] def bar(a: Foo): Either[Error, Bar] def baz(a: Foo): State[Baz, Bar] def baz(a: Foo, b: Baz): (Bar, Baz)

Slide 29

Slide 29 text

A pattern emerges For functions f : A → B that “do something” besides returning a B, augment the return type def foo(a: Foo): Option[Bar] def bar(a: Foo): Either[Error, Bar] def baz(a: Foo): State[Baz, Bar] def baz(a: Foo, b: Baz): (Bar, Baz) Often referred to as effectful functions

Slide 30

Slide 30 text

A pattern emerges For functions f : A → B that “do something” besides returning a B, augment the return type def foo(a: Foo): Option[Bar] def bar(a: Foo): Either[Error, Bar] def baz(a: Foo): State[Baz, Bar] def baz(a: Foo, b: Baz): (Bar, Baz) Often referred to as effectful functions Option, Either, State etc. are effects

Slide 31

Slide 31 text

A pattern emerges For functions f : A → B that “do something” besides returning a B, augment the return type def foo(a: Foo): Option[Bar] def bar(a: Foo): Either[Error, Bar] def baz(a: Foo): State[Baz, Bar] def baz(a: Foo, b: Baz): (Bar, Baz) Often referred to as effectful functions Option, Either, State etc. are effects Values without these behaviors are pure

Slide 32

Slide 32 text

Working with effects val x: Option[Int] = parseInt(...) val y = x match { case Some(int) => int max 0 case None => ??? }

Slide 33

Slide 33 text

Working with effects val x: Option[Int] = parseInt(...) val y: Option[Int] = x match { case Some(int) => Some(int max 0) case None => None }

Slide 34

Slide 34 text

Working with effects val x: Either[String, Host] = getKey[Host]("host") val y = x match { case Right(host) => host / "api" case Left(err) => ??? }

Slide 35

Slide 35 text

Working with effects val x: Either[String, Host] = getKey[Host]("host") val y: Either[String, Endpoint] = x match { case Right(host) => Right(host / "api") case Left(err) => Left(err) }

Slide 36

Slide 36 text

Working with effects val x: State[Long, Int] = randomInt val y: State[Long, Double] = State { (seed: Long) => ??? }

Slide 37

Slide 37 text

Working with effects val x: State[Long, Int] = randomInt val y: State[Long, Double] = State { (seed: Long) => val (randomInt, newSeed) = x.run(input) ??? }

Slide 38

Slide 38 text

Working with effects val x: State[Long, Int] = randomInt val y: State[Long, Double] = State { (seed: Long) => val (randomInt, newSeed) = x.run(input) (1.0 / randomInt, newSeed) }

Slide 39

Slide 39 text

Working with effects Apply a pure function f : A → B to an effectful value F[A]

Slide 40

Slide 40 text

Working with effects Apply a pure function f : A → B to an effectful value F[A] “Inside” F[A] we apply the function to the value and propagate some extra bits through, giving F[B]

Slide 41

Slide 41 text

Working with effects Apply a pure function f : A → B to an effectful value F[A] “Inside” F[A] we apply the function to the value and propagate some extra bits through, giving F[B] These “extra bits” tend to be the “something” effectful functions do

Slide 42

Slide 42 text

Functor trait Functor[F[_]] { def map[A, B](fa: F[A])(f: A => B): F[B] }

Slide 43

Slide 43 text

Functor new Functor[Option] { def map[A, B](fa: Option[A])(f: A => B): Option[B] = fa match { case Some(a) => Some(f(a)) case None => None } }

Slide 44

Slide 44 text

new Functor[Either[E, ?]] { def map[A, B](fa: Either[E, A])(f: A => B): Either[E, B] fa match { case Right(a) => Right(f(a)) case Left(e) => Left(e) } } 1 1? syntax is enabled by the kind-projector compiler plugin https://github.com/non/kind-projector

Slide 45

Slide 45 text

new Functor[State[S, ?]] { def map[A, B](fa: State[S, A])(f: A => B): State[S, B] = State { currentState => val (a, nextState) = fa.run(currentState) (f(a), nextState) } }

Slide 46

Slide 46 text

val i: Option[Int] = parseInt(...) val bounded: Option[Int] = i.map(_ max 0) val path: Either[String, Host] = getKey[Host]("host") val api: Either[String, Endpoint] = path.map(_ / "api") val rint: State[Long, Int] = randomInt val rdouble: State[Long, Double] = rint.map(i => 1.0 / i)

Slide 47

Slide 47 text

Working with a single effectful value What if we want to work with two or more effectful values?

Slide 48

Slide 48 text

Working with a single effectful value What if we want to work with two or more effectful values? Apply a pure n-ary function to n effectful values

Slide 49

Slide 49 text

Working with a single effectful value What if we want to work with two or more effectful values? Apply a pure n-ary function to n effectful values Focus on tupling the values - (F[A], F[B]) => F[(A, B)]

Slide 50

Slide 50 text

Working with a single effectful value What if we want to work with two or more effectful values? Apply a pure n-ary function to n effectful values Focus on tupling the values - (F[A], F[B]) => F[(A, B)]

Slide 51

Slide 51 text

Working with a single effectful value What if we want to work with two or more effectful values? Apply a pure n-ary function to n effectful values Focus on tupling the values - (F[A], F[B]) => F[(A, B)] val fa: F[A] = ... val fb: F[B] = ... val fab: F[(A, B)] = ???

Slide 52

Slide 52 text

Working with a single effectful value What if we want to work with two or more effectful values? Apply a pure n-ary function to n effectful values Focus on tupling the values - (F[A], F[B]) => F[(A, B)] val fa: F[A] = ... val fb: F[B] = ... val fab: F[(A, B)] = fa.map(a => ???)

Slide 53

Slide 53 text

Working with a single effectful value What if we want to work with two or more effectful values? Apply a pure n-ary function to n effectful values Focus on tupling the values - (F[A], F[B]) => F[(A, B)] val fa: F[A] = ... val fb: F[B] = ... val fab: F[(A, B)] = fa.map(a => fb.map(b => (a, b)) // ^ F[F[(A, B)]]

Slide 54

Slide 54 text

Working with multiple effectful values

Slide 55

Slide 55 text

Working with multiple effectful values def pairOption[A, B] (oa: Option[A], ob: Option[B]): Option[(A, B)] = (oa, ob) match { case (Some(a), Some(b)) => Some((a, b)) case _ => None }

Slide 56

Slide 56 text

Working with multiple effectful values def pairEither[E, A, B] (ea: Either[E, A], eb: Either[E, B]): Either[E, (A, B)] = (ea, eb) match { case (Right(a), Right(b)) => Right((a, b)) case (Left(e), _) => Left(e) case (_, Left(e)) => Left(e) }

Slide 57

Slide 57 text

Working with multiple effectful values def pairState[S, A, B] (sa: State[S, A], sb: State[S, B]): State[S, (A, B)] = State { currentState => ??? }

Slide 58

Slide 58 text

Working with multiple effectful values def pairState[S, A, B] (sa: State[S, A], sb: State[S, B]): State[S, (A, B)] = State { currentState => val (a, nextState) = sa.run(currentState) ??? }

Slide 59

Slide 59 text

Working with multiple effectful values def pairState[S, A, B] (sa: State[S, A], sb: State[S, B]): State[S, (A, B)] = State { currentState => val (a, nextState) = sa.run(currentState) val (b, finalState) = sb.run(nextState) ??? }

Slide 60

Slide 60 text

Working with multiple effectful values def pairState[S, A, B] (sa: State[S, A], sb: State[S, B]): State[S, (A, B)] = State { currentState => val (a, nextState) = sa.run(currentState) val (b, finalState) = sb.run(nextState) ((a, b), finalState) }

Slide 61

Slide 61 text

Applicative trait Applicative[F[_]] extends Functor[F] { def zip[A, B](fa: F[A], fb: F[B]): F[(A, B)] def pure[A](a: A): F[A] def map[A, B](fa: F[A])(f: A => B): F[B] }

Slide 62

Slide 62 text

new Applicative[Option] { def zip[A, B] (fa: Option[A], fb: Option[B]): Option[(A, B)] = (fa, fb) match { case (Some(a), Some(b)) => Some((a, b)) case _ => None } def pure[A](a: A): Option[A] = Some(a) ... }

Slide 63

Slide 63 text

new Applicative[Either[E, ?]] { def zip[A, B] (fa: Either[E, A], fb: Either[E, B]): Either[E, (A, B)] (fa, fb) match { case (Right(a), Right(b)) => Right((a, b)) case (Left(e), _) => Left(e) case (_, Left(e)) => Left(e) } def pure[A](a: A): Either[E, A] = Right(a) ... }

Slide 64

Slide 64 text

new Applicative[State[S, ?]] { def zip[A, B] (fa: State[S, A], fb: State[S, B]): State[S, (A, B)] = State { currentState => val (a, nextState) = fa.run(currentState) val (b, finalState) = fb.run(nextState) ((a, b), finalState) } def pure[A](a: A): State[S, A] = State(s => (a, s)) ... }

Slide 65

Slide 65 text

Applicative trait Applicative[F[_]] extends Functor[F] { def zip[A, B](fa: F[A], fb: F[B]): F[(A, B)] def pure[A](a: A): F[A] def map[A, B](fa: F[A])(f: A => B): F[B] def ap[A, B, C](ff: F[A => B])(fa: F[A]): F[B] = map(zip(ff, fa)) { case (f, a) => f(a) } }

Slide 66

Slide 66 text

// Option[Int] parseInt(...).zip(parseInt(...)).map { case (x, y) => x max y } // Either[Error, Endpoint] getKey[Host]("host").zip(getKey[Port]("port")).map { case (host, p) => host :| p / "api" } // State[Long, Int] randomInt.zip(randomInt).map { case (x, y) => gcd(x, y) }

Slide 67

Slide 67 text

No content

Slide 68

Slide 68 text

def traverseOption[A, B] (as: List[A])(f: A => Option[B]): Option[List[B]] = as.foldRight(Option(List.empty[B])) { (a, bs) => (f(a), bs) match { case (Some(h), Some(t)) => Some(h :: t) case _ => None } }

Slide 69

Slide 69 text

def traverseEither[E, A, B] (as: List[A])(f: A => Either[E, B]): Either[E, List[B]] = as.foldRight(Either.right(List.empty[B])) { (a, bs) => (f(a), bs) match { case (Right(h), Right(t)) => Right(h :: t) case (Left(e), _) => Left(e) case (_, Left(e)) => Left(e) } }

Slide 70

Slide 70 text

def traverseList[F[_]: Applicative, A, B] (as: List[A])(f: A => F[B]): F[List[B]] = as.foldRight(pure[F](List.empty[B])) { (a, bs) => f(a).zip(bs).map { case (h, t) => h :: t } }

Slide 71

Slide 71 text

// traverseList[Option, A, B] def traverseOption[A, B] (as: List[A])(f: A => Option[B]): Option[List[B]] // traverseList[Either[E, ?], A, B] def traverseEither[A, B] (as: List[A])(f: A => Either[E, B]): Either[E, List[B]] // traverseList[State[S, ?], A, B] def traverseState[A, B] (as: List[A])(f: A => State[S, B]): State[S, List[B]]

Slide 72

Slide 72 text

Working with multiple dependent effectful values Applicative lets us work with independent effectful values

Slide 73

Slide 73 text

Working with multiple dependent effectful values Applicative lets us work with independent effectful values F[A] and F[B] are given up front

Slide 74

Slide 74 text

Working with multiple dependent effectful values Applicative lets us work with independent effectful values F[A] and F[B] are given up front What if an F[B] is dependent on the value of an F[A]?

Slide 75

Slide 75 text

Working with multiple dependent effectful values Applicative lets us work with independent effectful values F[A] and F[B] are given up front What if an F[B] is dependent on the value of an F[A]?

Slide 76

Slide 76 text

Working with multiple dependent effectful values Applicative lets us work with independent effectful values F[A] and F[B] are given up front What if an F[B] is dependent on the value of an F[A]? def nextStep(x: Foo): F[Bar] val foo: F[Foo] = ... val bar: F[Bar] = ???

Slide 77

Slide 77 text

Working with multiple dependent effectful values Applicative lets us work with independent effectful values F[A] and F[B] are given up front What if an F[B] is dependent on the “result” of an F[A]? def nextStep(x: Foo): F[Bar] val foo: F[Foo] = ... val bar: F[Bar] = foo.map(x => ???)

Slide 78

Slide 78 text

Working with multiple dependent effectful values Applicative lets us work with independent effectful values F[A] and F[B] are given up front What if an F[B] is dependent on the “result” of an F[A]? def nextStep(x: Foo): F[Bar] val foo: F[Foo] = ... val bar: F[Bar] = foo.map(x => nextStep(x)) // ^ F[F[Bar]]

Slide 79

Slide 79 text

Working with multiple dependent effectful values

Slide 80

Slide 80 text

Working with multiple dependent effectful values def flatMapOption[A, B] (oa: Option[A])(f: A => Option[B]): Option[B] = oa match { case None => None case Some(a) => f(a) }

Slide 81

Slide 81 text

Working with multiple dependent effectful values def flatMapEither[E, A, B] (ea: Either[E, A])(f: A => Either[E, B]): Either[E, B] = ea match { case Left(e) => Left(e) case Right(a) => f(a) }

Slide 82

Slide 82 text

Working with multiple dependent effectful values def flatMapState[S, A, B] (sa: State[S, A])(f: A => State[S, B]): State[S, B] = State { currentState => ??? }

Slide 83

Slide 83 text

Working with multiple dependent effectful values def flatMapState[S, A, B] (sa: State[S, A])(f: A => State[S, B]): State[S, B] = State { currentState => val (a, nextState) = sa.run(currentState) ??? }

Slide 84

Slide 84 text

Working with multiple dependent effectful values def flatMapState[S, A, B] (sa: State[S, A])(f: A => State[S, B]): State[S, B] = State { currentState => val (a, nextState) = sa.run(currentState) val nextComputation = f(a) ??? }

Slide 85

Slide 85 text

Working with multiple dependent effectful values def flatMapState[S, A, B] (sa: State[S, A])(f: A => State[S, B]): State[S, B] = State { currentState => val (a, nextState) = sa.run(currentState) val nextComputation = f(a) nextComputation.run(nextState) }

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

// Option[Data] parseHeader(...).flatMap(header => parseBody(header, ...)) // Either[Error, DeploymentUnit] getKey[String]("type").flatMap { value => if (value == "service") getKey[Service]("docker") else getKey[Job]("job") } // State[Long, Int] randomInt.flatMap(i => boundedRandomInt(i))

Slide 89

Slide 89 text

Juggling effects We now have a vocabulary for working with effects

Slide 90

Slide 90 text

Juggling effects We now have a vocabulary for working with effects Functors: a single effect

Slide 91

Slide 91 text

Juggling effects We now have a vocabulary for working with effects Functors: a single effect Applicatives: multiple independent effects

Slide 92

Slide 92 text

Juggling effects We now have a vocabulary for working with effects Functors: a single effect Applicatives: multiple independent effects Monads: multiple dependent effects

Slide 93

Slide 93 text

username.flatMap { user => password.flatMap { pass => authenticate(user, pass).flatMap { token => newsFeed(token).zip(friendsList(token)).map { case (feed, friends) => render(feed, friends) } } } }

Slide 94

Slide 94 text

for { user <- username pass <- password token <- authenticate(user, pass) data <- newsFeed(token).zip(friendsList(token)) (feed, friends) = data } yield render(feed, friends)

Slide 95

Slide 95 text

What we didn’t have time to cover Laws - what makes a “good” functor, applicative, monad?

Slide 96

Slide 96 text

What we didn’t have time to cover Laws - what makes a “good” functor, applicative, monad? Computation with more than one effect

Slide 97

Slide 97 text

What we didn’t have time to cover Laws - what makes a “good” functor, applicative, monad? Computation with more than one effect Tracking IO as an effect

Slide 98

Slide 98 text

Tip of the iceberg Tomorrow @ 11:40 “Functional Programming with Effects” with Rob Norris

Slide 99

Slide 99 text

Tip of the iceberg Tomorrow @ 11:40 “Functional Programming with Effects” with Rob Norris “Functional Programming in Scala” by Runar Bjarnason and Paul Chiusano

Slide 100

Slide 100 text

Tip of the iceberg Tomorrow @ 11:40 “Functional Programming with Effects” with Rob Norris “Functional Programming in Scala” by Runar Bjarnason and Paul Chiusano Fine-grained effects, finally tagless, Free monads

Slide 101

Slide 101 text

Tip of the iceberg Tomorrow @ 11:40 “Functional Programming with Effects” with Rob Norris “Functional Programming in Scala” by Runar Bjarnason and Paul Chiusano Fine-grained effects, finally tagless, Free monads Functional programming beyond code

Slide 102

Slide 102 text

Tip of the iceberg Tomorrow @ 11:40 “Functional Programming with Effects” with Rob Norris “Functional Programming in Scala” by Runar Bjarnason and Paul Chiusano Fine-grained effects, finally tagless, Free monads Functional programming beyond code Nix(OS): The purely functional Linux distribution and package manager

Slide 103

Slide 103 text

Tip of the iceberg Tomorrow @ 11:40 “Functional Programming with Effects” with Rob Norris “Functional Programming in Scala” by Runar Bjarnason and Paul Chiusano Fine-grained effects, finally tagless, Free monads Functional programming beyond code Nix(OS): The purely functional Linux distribution and package manager Reproducible builds, immutable infrastructure, reproducible environments

Slide 104

Slide 104 text

Tip of the iceberg Tomorrow @ 11:40 “Functional Programming with Effects” with Rob Norris “Functional Programming in Scala” by Runar Bjarnason and Paul Chiusano Fine-grained effects, finally tagless, Free monads Functional programming beyond code Nix(OS): The purely functional Linux distribution and package manager Reproducible builds, immutable infrastructure, reproducible environments “Nelson: Rigorous Deployment for a Functional World” - Saturday @ 9:50 with Tim Perrett

Slide 105

Slide 105 text

EOF