November 16, 2017
370

Functors, applicatives, and monads are fundamental tools for some programmers, yet for many others they are considered immaterial. Indeed there are extremely few languages which offer support for even talking about these concepts. Why then are these programmers so fixated on them? What about them makes them so desirable and necessary? In this talk we will explore the what and why of these concepts and hopefully leave you understanding, if not convinced of, their utility.

November 16, 2017

Transcript

the Bay 2017

3. 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
4. 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
5. 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
6. 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))
7. 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)

9. Implications No null or exception throwing No mutation, print statements,

or side eﬀects in general
10. Implications No null or exception throwing No mutation, print statements,

or side eﬀects in general
11. Implications No null or exception throwing No mutation, print statements,

or side eﬀects in general var x = 0 x += 1 x += 1
12. Implications No null or exception throwing No mutation, print statements,

or side eﬀects in general var x = 0 x += 1 x += 1 var x = 0 val e = x += 1 e e

14. Getting back what we lost: null The concept of null

is useful, an absence of a value
15. 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
16. 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
17. 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]
18. Getting back what we lost: throwing exceptions Some functions we

want to write will be partial in nature
19. 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]
20. 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]
21. 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]
22. Getting back what we lost: mutation Many computations need state

they can read and write to
23. 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
24. 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
25. 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))
26. A pattern emerges For functions f : A → B

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

that “do something” besides returning a B, augment the return type
28. 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)
29. 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 eﬀectful functions
30. 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 eﬀectful functions Option, Either, State etc. are eﬀects
31. 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 eﬀectful functions Option, Either, State etc. are eﬀects Values without these behaviors are pure
32. Working with eﬀects val x: Option[Int] = parseInt(...) val y

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

Option[Int] = x match { case Some(int) => Some(int max 0) case None => None }
34. Working with eﬀects val x: Either[String, Host] = getKey[Host]("host") val

y = x match { case Right(host) => host / "api" case Left(err) => ??? }
35. Working with eﬀects 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) }
36. Working with eﬀects val x: State[Long, Int] = randomInt val

y: State[Long, Double] = State { (seed: Long) => ??? }
37. Working with eﬀects val x: State[Long, Int] = randomInt val

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

y: State[Long, Double] = State { (seed: Long) => val (randomInt, newSeed) = x.run(input) (1.0 / randomInt, newSeed) }
39. Working with eﬀects Apply a pure function f : A

→ B to an eﬀectful value F[A]
40. Working with eﬀects Apply a pure function f : A

→ B to an eﬀectful value F[A] “Inside” F[A] we apply the function to the value and propagate some extra bits through, giving F[B]
41. Working with eﬀects Apply a pure function f : A

→ B to an eﬀectful 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” eﬀectful functions do

B): F[B] }
43. 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 } }
44. 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
45. 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) } }
46. 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)
47. Working with a single eﬀectful value What if we want

to work with two or more eﬀectful values?
48. Working with a single eﬀectful value What if we want

to work with two or more eﬀectful values? Apply a pure n-ary function to n eﬀectful values
49. Working with a single eﬀectful value What if we want

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

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

to work with two or more eﬀectful values? Apply a pure n-ary function to n eﬀectful 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)] = ???
52. Working with a single eﬀectful value What if we want

to work with two or more eﬀectful values? Apply a pure n-ary function to n eﬀectful 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 => ???)
53. Working with a single eﬀectful value What if we want

to work with two or more eﬀectful values? Apply a pure n-ary function to n eﬀectful 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)]]

55. Working with multiple eﬀectful 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 }
56. Working with multiple eﬀectful 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) }
57. Working with multiple eﬀectful values def pairState[S, A, B] (sa:

State[S, A], sb: State[S, B]): State[S, (A, B)] = State { currentState => ??? }
58. Working with multiple eﬀectful 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) ??? }
59. Working with multiple eﬀectful 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) ??? }
60. Working with multiple eﬀectful 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) }
61. 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] }
62. 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) ... }
63. 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) ... }
64. 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)) ... }
65. 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) } }
66. // 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) }
67. None
68. 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 } }
69. 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) } }
70. 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 } }
71. // 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]]
72. Working with multiple dependent eﬀectful values Applicative lets us work

with independent eﬀectful values
73. Working with multiple dependent eﬀectful values Applicative lets us work

with independent eﬀectful values F[A] and F[B] are given up front
74. Working with multiple dependent eﬀectful values Applicative lets us work

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

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

with independent eﬀectful 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] = ???
77. Working with multiple dependent eﬀectful values Applicative lets us work

with independent eﬀectful 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 => ???)
78. Working with multiple dependent eﬀectful values Applicative lets us work

with independent eﬀectful 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]]

80. Working with multiple dependent eﬀectful values def flatMapOption[A, B] (oa:

Option[A])(f: A => Option[B]): Option[B] = oa match { case None => None case Some(a) => f(a) }
81. Working with multiple dependent eﬀectful 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) }
82. Working with multiple dependent eﬀectful values def flatMapState[S, A, B]

(sa: State[S, A])(f: A => State[S, B]): State[S, B] = State { currentState => ??? }
83. Working with multiple dependent eﬀectful 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) ??? }
84. Working with multiple dependent eﬀectful 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) ??? }
85. Working with multiple dependent eﬀectful 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) }

A => F[B]): F[B] def pure[A](a: A): F[A] }

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))) }

{ value => if (value == "service") getKey[Service]("docker") else getKey[Job]("job") } // State[Long, Int] randomInt.flatMap(i => boundedRandomInt(i))

eﬀects
90. Juggling eﬀects We now have a vocabulary for working with

eﬀects Functors: a single eﬀect
91. Juggling eﬀects We now have a vocabulary for working with

eﬀects Functors: a single eﬀect Applicatives: multiple independent eﬀects
92. Juggling eﬀects We now have a vocabulary for working with

eﬀects Functors: a single eﬀect Applicatives: multiple independent eﬀects Monads: multiple dependent eﬀects

{ token => newsFeed(token).zip(friendsList(token)).map { case (feed, friends) => render(feed, friends) } } } }

authenticate(user, pass) data <- newsFeed(token).zip(friendsList(token)) (feed, friends) = data } yield render(feed, friends)
95. What we didn’t have time to cover Laws - what

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

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

makes a “good” functor, applicative, monad? Computation with more than one eﬀect Tracking IO as an eﬀect
98. Tip of the iceberg Tomorrow @ 11:40 “Functional Programming with

Eﬀects” with Rob Norris
99. Tip of the iceberg Tomorrow @ 11:40 “Functional Programming with

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

Eﬀects” with Rob Norris “Functional Programming in Scala” by Runar Bjarnason and Paul Chiusano Fine-grained eﬀects, ﬁnally tagless, Free monads
101. Tip of the iceberg Tomorrow @ 11:40 “Functional Programming with

Eﬀects” with Rob Norris “Functional Programming in Scala” by Runar Bjarnason and Paul Chiusano Fine-grained eﬀects, ﬁnally tagless, Free monads Functional programming beyond code
102. Tip of the iceberg Tomorrow @ 11:40 “Functional Programming with

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

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

Eﬀects” with Rob Norris “Functional Programming in Scala” by Runar Bjarnason and Paul Chiusano Fine-grained eﬀects, ﬁnally 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