Applicative and Traversable Functors

An example from the standard library

One of the most useful functions when working with scala.concurrent.Future is Future.traverse, presented below in a simplified form.

import scala.concurrent.{ExecutionContext, Future}

def traverseFuture[A, B](as: List[A])(f: A => Future[B])(implicit ec: ExecutionContext): Future[List[B]] =
  Future.traverse(as)(f)

traverseFuture takes a List[A] and for each A in the list applies the function f to it, gathering results as it goes along. f is often referred to as an effectful function, where the Future effect is running the computation concurrently, presumably on another thread. This effect is apparent in the result of the function, which has gathered results inside Future.

But what if the effect we wanted wasn't Future? What if instead of concurrency for our effect we wanted validation (Option, Either, Validated) or State ? It turns out we can abstract out the commonalities between all these data types and write a generic traverse function once and for all. We can even go further and abstract over data types that can be traversed over such as List, Vector, and Option.

In this series we will build up the machinery needed to generalize the standard library's Future.traverse into its fully abstract and most reusable form.

If you'd like to read the published literature on these ideas, some good starting points are "Applicative Programming with Effects" by McBride and Patterson, and "The Essence of the Iterator Pattern" by Gibbons and Oliveira.