I'm new to Cats and I don't know how to overcome this situation. In the code bellow:
class Example[F[_]] {
import cats._
import cats.data._
import cats.syntax.all._
def saveAll(list: List[String])(implicit M: Monad[F]): F[List[String]] = {
val result: List[F[String]] =
list.map(saveOne)
}
def saveOne(s: String)(implicit M: Monad[F]): F[String] = s"Saved $s".pure[F]
}
How do I transform the result
variable, in the saveAll
function, to make sure it matches its expected return type?
Thanks.
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]
}