12 Applicative and traversable functors

 

This chapter covers

  • Introducing applicative functors
  • Introducing traversable functors
  • Defining semigroups

In the previous chapter on monads, we saw how many of the functions we’ve been writing for different combinator libraries can be expressed in terms of a single interface: Monad. Monads provide a powerful interface, as evidenced by the fact that we can use flatMap to essentially write imperative programs in a purely functional way.

In this chapter, we’ll learn about a type of related abstractions, applicative functors, which are less powerful than monads but more general (and hence more common). The process of arriving at applicative functors will also provide some insight into how to discover such abstractions, and we’ll use some of these ideas to uncover another useful abstraction: traversable functors. It may take some time for the full significance and usefulness of these abstractions to sink in, but you’ll see them popping up again and again in your daily work with FP if you pay attention.

12.1 Generalizing monads

By now we’ve seen various operations, like sequence and traverse, implemented many times for different monads, and in the last chapter, we generalized the implementations to work for any monad F:

def sequence[A](fas: List[F[A]]): F[List[A]]
  traverse(fas)(fa => fa)
 
def traverse[A, B](as: List[A])(f: A => F[B]): F[List[B]]
  as.foldRight(unit(List[B]()))((a, acc) => f(a).map2(acc)(_ :: _))

12.2 The Applicative trait

12.3 The difference between monads and applicative functors

12.3.1 The Option applicative versus the Option monad

12.3.2 The Parser applicative versus the Parser monad

12.4 The advantages of applicative functors

12.4.1 Not all applicative functors are monads

12.5 The applicative laws

12.5.1 Left and right identity

12.5.2 Associativity