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)
Related
I'm reading the scala-with-cats book and follow it's exercise. When I come to the case study: data validation, I encounter some problems.
Here is my entire code (just the same with the book):
package org.scala.ch10.final_recap
import cats.Semigroup
import cats.data.Validated
import cats.data.Validated._
import cats.data.Kleisli
import cats.data.NonEmptyList
import cats.instances.either._
import cats.syntax.apply._
import cats.syntax.semigroup._
import cats.syntax.validated._
sealed trait Predicate[E, A] {
import Predicate._
def and(that: Predicate[E, A]): Predicate[E, A] =
And(this, that)
def or(that: Predicate[E, A]): Predicate[E, A] =
Or(this, that)
/**
* This part is for Kleislis
* #return
*/
def run(implicit s: Semigroup[E]): A => Either[E, A] =
(a: A) => this(a).toEither
def apply(a: A)(implicit s: Semigroup[E]): Validated[E, A] =
this match {
case Pure(func) =>
func(a)
case And(left, right) => (left(a), right(a)).mapN((_, _) => a)
case Or(left, right) =>
left(a) match {
case Valid(_) => Valid(a)
case Invalid(e1) =>
right(a) match {
case Valid(_) => Invalid(e1)
case Invalid(e2) => Invalid(e1 |+| e2)
}
}
}
}
object Predicate {
final case class And[E, A](left: Predicate[E, A], right: Predicate[E, A]) extends Predicate[E, A]
final case class Or[E, A](left: Predicate[E, A], right: Predicate[E, A]) extends Predicate[E, A]
final case class Pure[E, A](func: A => Validated[E, A]) extends Predicate[E, A]
def apply[E, A](f: A => Validated[E, A]): Predicate[E, A] = Pure(f)
def lift[E, A](err: E, fn: A => Boolean): Predicate[E, A] = Pure(a => if(fn(a)) a.valid else err.invalid)
}
object FinalRecapPredicate {
type Errors = NonEmptyList[String]
def error(s: String): NonEmptyList[String] = NonEmptyList(s, Nil)
type Result[A] = Either[Errors, A]
type Check[A, B] = Kleisli[Result, A, B]
def check[A, B](func: A => Result[B]): Check[A, B] = Kleisli(func)
def checkPred[A](pred: Predicate[Errors, A]): Check[A, A] =
Kleisli[Result, A, A](pred.run)
def longerThan(n: Int): Predicate[Errors, String] =
Predicate.lift(
error(s"Must be longer than $n characters"),
str => str.length > n
)
val alphanumeric: Predicate[Errors, String] =
Predicate.lift(
error(s"Must be all alphanumeric characters"),
str => str.forall(_.isLetterOrDigit)
)
def contains(char: Char): Predicate[Errors, String] =
Predicate.lift(
error(s"Must contain the character $char"),
str => str.contains(char)
)
def containsOnce(char: Char): Predicate[Errors, String] =
Predicate.lift(
error(s"Must contain the character $char only once"),
str => str.count(_ == char) == 1
)
val checkUsername: Check[String, String] = checkPred(longerThan(3) and alphanumeric)
val splitEmail: Check[String, (String, String)] = check(_.split('#') match {
case Array(name, domain) =>
Right((name, domain))
case _ =>
Left(error("Must contain a single # character"))
})
val checkLeft: Check[String, String] = checkPred(longerThan(0))
val checkRight: Check[String, String] = checkPred(longerThan(3) and contains('.'))
val joinEmail: Check[(String, String), String] =
check {
case (l, r) => (checkLeft(l), checkRight(r)).mapN(_ + "#" + _)
}
val checkEmail: Check[String, String] = splitEmail andThen joinEmail
final case class User(username: String, email: String)
def createUser(username: String, email: String): Either[Errors, User] =
(checkUsername.run(username),
checkEmail.run(email)).mapN(User)
def main(args: Array[String]): Unit = {
println(createUser("", "noel#underscore.io#io"))
}
}
It supposes the code should end up with the error message Left(NonEmptyList(Must be longer than 3 characters), Must contain a single # character) But what I actually is Left(NonEmptyList(Must be longer than 3 characters))
Obviously, it does not work as expected. It fails fast instead of accumulating errors... How to fix that plz? (I've spent hours now and can't get a workaround)
This is the "problematic" part:
def createUser(username: String, email: String): Either[Errors, User] =
(checkUsername.run(username),
checkEmail.run(email)).mapN(User)
You are combining a tuple of Results, where
type Result[A] = Either[Errors, A]
This means you are really doing a mapN on a pair of Eithers, an operation provided by the Semigroupal type class. This operation will not accumulate results.
There are several reasons for this, but one that I find particularly important is the preserving of behaviour if we find ourselves using a Semigroupal / Applicative which also happens to be a Monad. Why is that a problem? Because Monads are sequencing operations, making each step depend on the previous one, and having "fail early" semantics. When using some Monad, one might expect those semantics to be preserved when using constructs from the underlying Applicative (every Monad is also an Applicative). In that case, if the implementation of Applicative used "accumulation" semantics instead of "fail early" semantics, we would ruin some important laws like referential transparency.
You can use a parallel version of mapN, called parMapN, whose contract guarantees that the implementation will be evaluating all results in parallel. This means that it definitely cannot be expected to have the "fail early" semantics, and accumulating results is fine in that case.
Note that Validated accumulates results as well, usually in a NonEmptyList or a NonEmptyChain. This is probably why you expected to see your accumulated results; the only problem is, you were not using Validated values in the "problematic" part of your code, but raw Eithers instead.
Here's some simple code that demonstrates the above concepts:
import cats.data._
import cats.implicits._
val l1: Either[String, Int] = Left("foo")
val l2: Either[String, Int] = Left("bar")
(l1, l2).mapN(_ + _)
// Left(foo)
(l1, l2).parMapN(_ + _)
// Left(foobar)
val v1: ValidatedNel[String, Int] = l1.toValidatedNel
val v2: ValidatedNel[String, Int] = l2.toValidatedNel
(v1, v2).mapN(_ + _)
// Invalid(NonEmptyList(foo, bar))
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)
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.
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 have data like this in an RDD:
RDD[((Int, Int, Int), ((Int, Int), Int))]
as:
(((9,679,16),((2,274),1)), ((250,976,13),((2,218),1)))
I want output as :
((9,679,16,2,274,1),(250,976,13,2,218,1))
After Joining 2 rdds with:
val joinSale = salesTwo.join(saleFinal)
I got that result set. I tried the following code.
joinSale.flatMap(x => x).take(100).foreach(println)
I have tried map/flatMap but couldn't do it. Any ideas how to implement a scenario like this ? Thanks in advance ..
You can do this with pattern matching in scala. Simply wrap your tuple modification logic within a map similar to the below:
val mappedJoinSale = joinSale.map { case ((a, b, c), ((d, e), f)) => (a, b, c, d, e, f) }
Using your example, we have:
scala> val example = sc.parallelize(Array(((9,679,16),((2,274),1)), ((250,976,13),((2,218),1))))
example: org.apache.spark.rdd.RDD[((Int, Int, Int), ((Int, Int), Int))] = ParallelCollectionRDD[0] at parallelize at <console>:12
scala> val mapped = example.map { case ((a, b, c), ((d, e), f)) => (a, b, c, d, e, f) }
mapped: org.apache.spark.rdd.RDD[(Int, Int, Int, Int, Int, Int)] = MappedRDD[1] at map at <console>:14
scala> mapped.take(2).foreach(println)
...
(9,679,16,2,274,1)
(250,976,13,2,218,1)
You could also create generic tuple flattener using marvelous shapeless library as follows:
import shapeless._
import shapeless.ops.tuple
trait LowLevelFlatten extends Poly1 {
implicit def anyFlat[T] = at[T](x => Tuple1(x))
}
object concat extends Poly2 {
implicit def atTuples[T1, T2](implicit prepend: tuple.Prepend[T1, T2]): Case.Aux[T1, T2, prepend.Out] =
at[T1,T2]((t1,t2) => prepend(t1,t2))
}
object flatten extends LowLevelFlatten {
implicit def tupleFlat[T, M](implicit
mapper: tuple.Mapper.Aux[T, flatten.type, M],
reducer: tuple.LeftReducer[M, concat.type]
): Case.Aux[T, reducer.Out] =
at[T](t => reducer(mapper(t)))
}
Now in any code where import shapeless._ exists you could use it as
joinSale.map(flatten)