Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The Functor, Applicative, Monad talk

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.

Adelbert Chang

November 16, 2017
Tweet

More Decks by Adelbert Chang

Other Decks in Programming

Transcript

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

    View Slide

  2. Assumptions
    We are interested in pure functional programming

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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)

    View Slide

  8. Implications
    No null or exception throwing

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  13. Getting back what we lost: null

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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]

    View Slide

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

    View Slide

  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]

    View Slide

  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]

    View Slide

  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]

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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)

    View Slide

  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 effectful functions

    View Slide

  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 effectful functions
    Option, Either, State etc. are effects

    View Slide

  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 effectful functions
    Option, Either, State etc. are effects
    Values without these behaviors are pure

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  40. 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]

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  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)

    View Slide

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

    View Slide

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

    View Slide

  49. 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)]

    View Slide

  50. 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)]

    View Slide

  51. 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)] =
    ???

    View Slide

  52. 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 => ???)

    View Slide

  53. 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)]]

    View Slide

  54. Working with multiple effectful values

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  58. 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)
    ???
    }

    View Slide

  59. 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)
    ???
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  67. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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]]

    View Slide

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

    View Slide

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

    View Slide

  74. 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]?

    View Slide

  75. 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]?

    View Slide

  76. 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] =
    ???

    View Slide

  77. 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 => ???)

    View Slide

  78. 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]]

    View Slide

  79. Working with multiple dependent effectful values

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  83. 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)
    ???
    }

    View Slide

  84. 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)
    ???
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  88. // 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))

    View Slide

  89. Juggling effects
    We now have a vocabulary for working with effects

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  105. EOF

    View Slide