380

# The Functor, Applicative, Monad talk

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

1. The Functor, Applicative, Monad talk
Scale by the Bay 2017

2. Assumptions
We are interested in pure functional programming

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)

8. Implications
No null or exception throwing

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

13. Getting back what we lost: null

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

42. Functor
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => 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)]]

54. Working with multiple eﬀectful values

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

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

69. 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
}
}

70. // 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]]

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

72. 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

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
What if an F[B] is dependent on the value of an F[A]?

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]?
def nextStep(x: Foo): F[Bar]
val foo: F[Foo] = ...
val bar: F[Bar] =
???

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 “result” of an F[A]?
def nextStep(x: Foo): F[Bar]
val foo: F[Foo] = ...
val bar: F[Bar] =
foo.map(x => ???)

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 => nextStep(x))
// ^ F[F[Bar]]

78. Working with multiple dependent eﬀectful values

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

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

81. 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 =>
???
}

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 =>
val (a, nextState) = sa.run(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)
val nextComputation = f(a)
???
}

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)
nextComputation.run(nextState)
}

def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
def pure[A](a: A): F[A]
}

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

87. // Option[Data]
// 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))

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

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

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

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

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

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

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

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

96. 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

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

98. 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

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
Fine-grained eﬀects, ﬁnally tagless, Free monads

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
Functional programming beyond code

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
Nix(OS): The purely functional Linux distribution and package
manager

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
Reproducible builds, immutable infrastructure, reproducible
environments

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
“Nelson: Rigorous Deployment for a Functional World” -
Saturday @ 9:50 with Tim Perrett

104. EOF