NonEmptyTraverse

API Documentation: NonEmptyTraverse

NonEmptyTraverse is a non-empty version of the Traverse type class, just like Reducible is a non-empty version of Foldable. As such, it extends both Reducible and Traverse in the type class hierarchy. It provides the nonEmptyTraverse and nonEmptySequence methods that require an instance of Apply instead of Applicative:

def nonEmptyTraverse[G[_]: Apply, A, B](fa: F[A])(f: A => G[B]): G[F[B]]

def nonEmptySequence[G[_]: Apply, A](fga: F[G[A]]): G[F[A]]

In the Applicative tutorial we learned of Apply as a weakened Applicative lacking the pure method. One example type lacking an Applicative instance is Map[K, *], it's impossible to implement a pure method for it.

Knowing this, we can make use of NonEmptyTraverse, to traverse over a sequence of Maps. One example application one could think of is, when we have a list of text snippets, count the occurrence of each word in each snippet and return all the common words and their occurrences in each snippet:

import cats.syntax.all._
import cats.data.NonEmptyList

val snippets = NonEmptyList.of("What do you do", "What are you doing")
// snippets: NonEmptyList[String] = NonEmptyList(
//   head = "What do you do",
//   tail = List("What are you doing")
// )

def countWords(text: String): Map[String, Int] =
  text.split(" ").groupBy(identity).view.mapValues(_.length).toMap

snippets.nonEmptyTraverse(countWords)
// res0: Map[String, NonEmptyList[Int]] = Map(
//   "What" -> NonEmptyList(head = 1, tail = List(1)),
//   "you" -> NonEmptyList(head = 1, tail = List(1))
// )

Note that, just like traverse, nonEmptyTraverse(f) is equivalent to map(f).nonEmptySequence, so the above could be rewritten as:

snippets.map(countWords).nonEmptySequence
// res1: Map[String, NonEmptyList[Int]] = Map(
//   "What" -> NonEmptyList(head = 1, tail = List(1)),
//   "you" -> NonEmptyList(head = 1, tail = List(1))
// )

NonEmptyTraverse also offers flatNonEmptyTraverse and flatNonEmptySequence methods that are analogous to flatTraverse and flatSequence in Traverse. Just like with nonEmptyTraverse these methods don't require a Monad instance, but only a FlatMap, which is the weakened version of Monad without pure.