Given:
import cats.syntax.cartesian._
type M[X] = Future[Either[Error, X]]
val ma: M[A] = ???
val mb: M[B] = ???
I know I can do that:
def doStuff(a: A, b: B): C = ???
val result: M[C] = (ma |#| mb).map(doStuff)
But how do I flatMap? There's no flatMap in the CartesianBuilders.
def doFancyStuff(a: A, b: B): M[C] = ???
val result: M[C] = (ma |#| mb).flatMap(doFancyStuff)
I think the main problem is that it's awkward to flatMap on a Future[Either[Error, X]] in the sense of EitherT[Future, Error, X]-monad stack, because the original flatMap of the Future gets in the way, and the compiler isn't looking for a monad instance that could handle the combination of Future and Either simultaneously. However, if you wrap the futures in EitherT, everything works smoothly.
For cats 1.0.1
In the following, (a,b).tupled corresponds to Cartesian.product(a, b), and (a, b).mapN corresponds to the deprecated (a |#| b).map.
Given types A, B, C, Error, the following code snippets compile under cats 1.0.1:
If you want to preserve your definition of M (you probably should), then you
can avoid the above mentioned problems by wrapping everything in EitherT and then
extracting the value:
import scala.util.Either
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global // required by Monad[Future]
import cats.instances.future._ // Monad for `Future`
import cats.syntax.apply._ // `tupled` and `mapN`
import cats.data.EitherT // EitherT monad transformer
type M[X] = Future[Either[Error, X]]
val ma: M[A] = ???
val mb: M[B] = ???
def doStuff(a: A, b: B): C = ???
val result1: M[C] = (EitherT(ma), EitherT(mb)).mapN(doStuff).value
def doFancyStuff(a: A, b: B): M[C] = ???
val result2: M[C] = (for {
ab <- (EitherT(ma), EitherT(mb)).tupled
c <- EitherT(doFancyStuff(ab._1, ab._2))
} yield c).value
However, if this seems too awkward, and you can adjust the definition of M,
the following variant could be slightly shorter:
import scala.util.Either
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global // required by Monad[Future]
import cats.instances.future._ // Monad for `Future`
import cats.syntax.apply._ // `tupled` and `mapN`
import cats.data.EitherT // EitherT monad transformer
type M[X] = EitherT[Future, Error, X]
val ma: M[A] = ??? // either adjust signatures, or wrap result in EitherT(res)
val mb: M[B] = ???
def doStuff(a: A, b: B): C = ???
val result1: M[C] = (ma, mb).mapN(doStuff)
def doFancyStuff(a: A, b: B): M[C] = ???
val result2: M[C] = (ma, mb).tupled.flatMap{
case (a, b) => doFancyStuff(a, b)
}
This is because (ma, mb).tupled builds a M[(A, B)], where M is the previously mentioned monad stack, which then can be easily flatMapped with a (A, B) => M[C] function to M[C].
For older versions with Cartesian (untested)
Assuming that (ma, mb).tupled corresponds to the deprecated Cartesian.product and (ma, mb).mapN corresponds to the deprecated (ma |#| mb).map, the two definitions of result1 and result2 in the above code snippet for 1.0.1 translate to:
val result1: M[C] = (ma |#| mb).map(doStuff)
val result2: M[C] = Cartesian[M].product(ma, mb).flatMap{
case (a, b) => doFancyStuff(a, b)
}
Again, this works only because Cartesian[M].product(ma, mb) builds an M[(A, B)] from M[A] and M[B], where M[X] is defined as EitherT[Future, Error, X]. If it were defined as Future[Either[Error, X]], then the flatMap would be invoked on the Future, and instead of doFancyStuff we would have to pass something like Either[Error, (A, B)] => Future[Either[Error, C]], which is probably not what you want.
Related
I've started learning functional programming with Cats and I stuck with flatMapping (merging) applicatives F[List].
What is very simple in pure Scala is flatmapping list of lists like that:
val animals = List("Dog", "Cat", "Bird")
def getBreads(animal: String): List[String] = ...
val allAnimalsBreads = animals.flatMap(animal => getBread(animal)) // this will be just List[String]
How can I do the same if everything is wrapped with applicative?:
val animals = List("Dog", "Cat", "Bird").pure[F]
def getBreads(animal: String): F[List[String]] = ...
val allAnimalsBreads = ? // this should be F[List[String]]
Applicative provides ap and pure, but does not guarantee to provide flatMap, which is provided by Monad:
Monad extends the Applicative type class with a new function flatten.
If F was a monad, then at least in scalaz we might use ListT, for example,
import scalaz._
import ListT._
import scalaz.std.option._
val animals: Option[List[String]] = Some(List("Dog", "Cat", "Bird"))
def getBreeds(animal: String): Option[List[String]] = ???
(for {
animal <- listT(animals)
breed <- listT(getBreeds(animal))
} yield breed).run
However cats does not seem to provide ListT:
A naive implementation of ListT suffers from associativity issues; ... It’s possible to create a ListT that doesn’t
have these issues, but it tends to be pretty inefficient. For many
use-cases, Nested can be used to achieve the desired results.
Here is an attempt at a mad solution that you should not use. Consider Validated which only has Applicative instance. Let us provide a Monad instance even though Validated is not a Monad:
implicit def validatedMonad[E]: Monad[Validated[E, *]] =
new Monad[Validated[E, *]] {
def flatMap[A, B](fa: Validated[E, A])(f: A => Validated[E, B]): Validated[E, B] =
fa match {
case Valid(a) => f(a)
case i # Invalid(_) => i
}
def pure[A](x: A): Validated[E, A] = Valid(x)
def tailRecM[A, B](a: A)(f: A => Validated[E, Either[A, B]]) = ???
}
The implementation of validatedMonad is taken from scala-exercises.org/cats/validated.
Next let us make scalaz's listT available within cats via shims interop layer
libraryDependencies += "com.codecommit" %% "shims" % "2.1.0"
Putting it all together, we have
import cats._
import cats.Monad
import cats.data.Validated.{Invalid, Valid}
import cats.data.{Nested, OptionT, Validated, ValidatedNec}
import cats.implicits._
import scalaz.ListT._
import shims._
implicit def validatedMonad[E]: Monad[Validated[E, *]] =
new Monad[Validated[E, *]] {
def flatMap[A, B](fa: Validated[E, A])(f: A => Validated[E, B]): Validated[E, B] =
fa match {
case Valid(a) => f(a)
case i # Invalid(_) => i
}
def pure[A](x: A): Validated[E, A] = Valid(x)
def tailRecM[A, B](a: A)(f: A => Validated[E, Either[A, B]]) = ???
}
val animals: Validated[String, List[String]] = List("Dog", "Cat", "Bird").valid
def getBreeds(animal: String): Validated[String, List[String]] = ???
(for {
animal <- listT(animals)
breed <- listT(getBreeds(animal))
} yield breed).run
Note this "solution" breaks monadic laws, is not general, and is likely to cause confusion, so do not use it.
For Applicative this is impossible. For Monad if you don't want to use unconventional transformer ListT you can do
import cats.syntax.traverse._
import cats.syntax.applicative._
import cats.syntax.functor._
import cats.syntax.flatMap._
import cats.instances.list._
val allAnimalsBreads: F[List[String]] =
animals.map(_.map(getBreads)) // F[List[F[List[String]]]]
.map(_.sequence) // F[F[List[List[String]]]]
.flatten // F[List[List[String]]]
.map(_.flatten) // F[List[String]]
Suppose I have the following setting:
def foo: Either[Error, A] = ???
def bar: EitherT[Future, Error, B] = ???
case class Baz(a: A, b: B)
How can I use for comprehension to instantiate the class Baz? I tried with:
val res = for {
a <- foo
b <- bar
} yield Baz(a, b)
but, the result has type Either[Error, Nothing]. I don't know what is the right return type in this case, but obviously I don't want Nothing...
What is the right way to combine Either and EitherT in for comprehension?
Use EitherT.fromEither function to create EitherT from Either
import cats.data._
import cats.implicits._
def foo[A]: Either[Error, A] = ???
def bar[B]: EitherT[Future, Error, B] = ???
case class Baz[A, B](a: A, b: B)
def res[A, B] = for {
a <- EitherT.fromEither[Future](foo[A])
b <- bar[B]
} yield Baz(a, b)
I know I can traverse Lists
import cats.instances.list._
import cats.syntax.traverse._
def doMagic(item: A): M[B] = ???
val list: List[A] = ???
val result: M[List[B]] = list.traverse(doMagic)
And I can convert a Seq back and forth to List
val seq: Seq[A] = ???
val result: M[Seq[B]] = seq.toList.traverse(doMagic).map(_.toSeq)
But can I also traverse Seq without the boilerplate?
val seq: Seq[A] = ???
val result: M[Seq[B]] = seq.traverse(doMagic)
Or what's an easy way to get an instance of Traverse[Seq]?
Cats does not provide typeclass instances for Seq, so besides implementing it yourself you're stuck with the conversion.
As to why, there's an ongoing discussion in an (somewhat old) Cats issue. To sum it up, you won't know enough about Seq underlying characteristics to make sure some of the typeclasses instances laws hold.
EDIT : Nevermind, it exists now, see linked thread
As of cats 2.3, support for immutable.Seq is now built in. See "Where are implicit instances for Seq?" on the FAQ or this PR where the functionality was added.
If you are absolutely sure that the conversion from all Seq to List will always succeed in your code, you can simply transfer the Traverse structure from List to Seq over an (pseudo-)isomorphism:
def traverseFromIso[F[_], Z[_]]
(forward: F ~> Z, inverse: Z ~> F)
(implicit zt: Traverse[Z])
: Traverse[F] = new Traverse[F] {
def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) ⇒ B): B = zt.foldLeft(forward(fa), b)(f)
def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
zt.foldRight(forward(fa), lb)(f)
def traverse[G[_], A, B]
(fa: F[A])
(f: (A) ⇒ G[B])
(implicit appG: Applicative[G])
: G[F[B]] = {
(zt.traverse(forward(fa))(f)(appG)).map(zb => inverse(zb))
}
}
This isn't really an isomorphism, because the conversion from Seq to List can fail badly (e.g. if the sequence is infinite). What it does is simply converting Seq to List back and forth, and forwarding all method calls to those of Traverse[List].
Now you can use this method to build an instance of Traverse[Seq]:
implicit val seqTraverse: Traverse[Seq] = traverseFromIso(
new FunctionK[Seq, List] { def apply[X](sx: Seq[X]): List[X] = sx.toList },
new FunctionK[List, Seq] { def apply[X](lx: List[X]): Seq[X] = lx }
)
Full code snippet (compiles with scala 2.12.4 and cats 1.0.1):
import cats._
import cats.implicits._
import cats.arrow.FunctionK
import scala.language.higherKinds
object TraverseFromIso {
// This method can build you a `Traversable[Seq]` from
// an `Traversable[List]` and a pair of polymorphic conversion
// functions:
def traverseFromIso[F[_], Z[_]]
(forward: F ~> Z, inverse: Z ~> F)
(implicit zt: Traverse[Z])
: Traverse[F] = new Traverse[F] {
def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) ⇒ B): B = zt.foldLeft(forward(fa), b)(f)
def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
zt.foldRight(forward(fa), lb)(f)
def traverse[G[_], A, B]
(fa: F[A])
(f: (A) ⇒ G[B])
(implicit appG: Applicative[G])
: G[F[B]] = {
(zt.traverse(forward(fa))(f)(appG)).map(zb => inverse(zb))
}
}
// A little demo
def main(args: Array[String]): Unit = {
// To instantiate a `Traverse[Seq]`, we have to provide
// two natural transformations (from List to Seq and back):
implicit val seqTraverse: Traverse[Seq] = traverseFromIso(
new FunctionK[Seq, List] { def apply[X](sx: Seq[X]): List[X] = sx.toList },
new FunctionK[List, Seq] { def apply[X](lx: List[X]): Seq[X] = lx }
)
// do stuff with `Traversable[Seq]` here
}
}
I am looking at this question from Scala and Scalaz angles. OptionT works for Future but not Try. What is the reason there is no OptionT for Try where there is a usecase a function i.e. def foo(i: Int): Try[Option[Int]] = ... may or not return a value and occasionally network exception will happen? Thanks
The reason is Try isn't a valid functor.
You'll need to use scalaz-outlaws or write your own Try instances. Here's a working example using scalaz-outlaws' Try instances:
import scala.util.{Try,Success,Failure}
import scalaz._
import Scalaz._
implicit val tryOutlawInstances = new Traverse[Try] with Monad[Try] with Plus[Try]{
def point[A](a: ⇒ A): Try[A] = Success(a)
override def map[A,B](fa: Try[A])(f: A ⇒ B) = fa map f
def bind[A,B](fa: Try[A])(f: A ⇒ Try[B]) = fa flatMap f
def traverseImpl[F[_], A, B](fa: Try[A])(f: A ⇒ F[B])(implicit F: Applicative[F]) : F[Try[B]] = fa match {
case Success(a) ⇒ F.map(f(a))(Success.apply)
case Failure(f) ⇒ F.point(Failure(f))
}
def plus[A](a: Try[A], b: ⇒ Try[A]) = a orElse b
}
val foo = Try("foo".some)
val result = OptionT(foo).map(x => x.toUpperCase).run
As the title mentions.
Having many operations done using EitherT[Future, A, B]. Sometimes I want map left or right through another operation having signature A => Future[C]. Other scenario is that EitherT[Future, A, B] the result of a mapping over a future resulting Future[EitherT[Future, A, B]].
How can I elegantly flatten types like:
EitherT[Future, Future[A], Future[B]] and Future[EitherT[Future, A, B]]
Thank you in advance.
In all your cases you can use EitherT#flatMap (or EitherT#flatMapF), in combination with lifting some value to EitherT (or disjunction (\/) with flatMapF).
Mapping a B => F[C] over an EitherT[F, A, B] :
flatMap + lift
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scalaz._, Scalaz._
def f(i: Int): Future[Double] = Future.successful(i.toDouble)
val r = EitherT.right[Future, String, Int](Future.successful(1))
r.flatMap(i => EitherT.right(f(i)))
// or
r.flatMapF(i => f(i).map(_.right))
Mapping a A => F[C] over an EitherT[F, A, B] :
swap + flatMap + lift
def g(s: String): Future[Int] = Future.successful(s.length)
val l = EitherT.left[Future, String, Int](Future.successful("error"))
l.swap.flatMap(s => EitherT.right(g(s))).swap
// or
l.swap.flatMap(s => EitherT.left[Future, Int, Int](g(s)))
// or
l.swap.flatMapF(s => g(s).map(_.left))
Mapping an A => Either[F, B, C] to an F[A] :
lift + flatMap
def h(i: Int): EitherT[Future, String, Int] =
EitherT.right(Future.successful(i + 1))
val fut = Future.successful(1)
// mapping gives us Future[EitherT[Future, String, Int]]
fut.map(h)
// lifting to EitherT and flatMap gives us EitherT[Future, String, Int]
EitherT.right(fut).flatMap(h)