This kind of transformation is done with the traverse
operation:
class Example[F[_]] {
import cats._
import cats.implicits._
def saveAll(list: List[String])(implicit M: Monad[F]): F[List[String]] =
list.traverse(saveOne)
def saveOne(s: String)(implicit M: Monad[F]): F[String] =
s"Saved $s".pure[F]
}
As you can see from the traverse
method signature in Traverse
typeclass it requires an instance of Applicative
, not a Monad
:
trait Traverse[F[_]] {
def traverse[G[_]: Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]]
}
In cats every type that has a Monad
also has an Applicative
, so the Example
class works even with Monad
.
But the reverse is not true. Some types only have an Applicative
instance. The most notable of those is Validated
. You can read more about the problem with implementing Monad
for Validated
in the cats documentation.
So this code will be more general if you request an instance of Applicative
instead:
class Example[F[_]] {
import cats._
import cats.implicits._
def saveAll(list: List[String])(implicit M: Applicative[F]): F[List[String]] =
list.traverse(saveOne)
def saveOne(s: String)(implicit M: Applicative[F]): F[String] =
s"Saved $s".pure[F]
}