From 66b4ac77cb657276b306da582985e7a2540f1108 Mon Sep 17 00:00:00 2001 From: ven Date: Fri, 29 Sep 2017 14:58:20 +0200 Subject: [PATCH] Implement EitherT#leftFlatMap and EitherT#leftSemiflatMap (#1790) * Implement EitherT#leftFlatMap and EitherT#leftSemiflatMap * Add additional tests for EitherT * Rename tests Tiny nit --- core/src/main/scala/cats/data/EitherT.scala | 12 ++++++++++ .../test/scala/cats/tests/EitherTTests.scala | 24 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/core/src/main/scala/cats/data/EitherT.scala b/core/src/main/scala/cats/data/EitherT.scala index 879b7ffb52..cbb9f174c5 100644 --- a/core/src/main/scala/cats/data/EitherT.scala +++ b/core/src/main/scala/cats/data/EitherT.scala @@ -96,6 +96,18 @@ final case class EitherT[F[_], A, B](value: F[Either[A, B]]) { def leftMap[C](f: A => C)(implicit F: Functor[F]): EitherT[F, C, B] = bimap(f, identity) + def leftFlatMap[BB >: B, D](f: A => EitherT[F, D, BB])(implicit F: Monad[F]): EitherT[F, D, BB] = + EitherT(F.flatMap(value) { + case Left(a) => f(a).value + case r@Right(_) => F.pure(r.leftCast) + }) + + def leftSemiflatMap[D](f: A => F[D])(implicit F: Monad[F]): EitherT[F, D, B] = + EitherT(F.flatMap(value) { + case Left(a) => F.map(f(a)) { d => Left(d) } + case r@Right(_) => F.pure(r.leftCast) + }) + def compare(that: EitherT[F, A, B])(implicit o: Order[F[Either[A, B]]]): Int = o.compare(value, that.value) diff --git a/tests/src/test/scala/cats/tests/EitherTTests.scala b/tests/src/test/scala/cats/tests/EitherTTests.scala index 01663ee5ac..3ae21c74e5 100644 --- a/tests/src/test/scala/cats/tests/EitherTTests.scala +++ b/tests/src/test/scala/cats/tests/EitherTTests.scala @@ -434,4 +434,28 @@ class EitherTTests extends CatsSuite { } yield s1 ++ s2 } + test("leftFlatMap consistent with leftMap") { + forAll { (eithert: EitherT[List, String, Int], f: String => String) => + eithert.leftFlatMap(v => EitherT.left[Int](List(f(v)))) should ===(eithert.leftMap(f)) + } + } + + test("leftFlatMap consistent with swap and then flatMap") { + forAll { (eithert: EitherT[List, String, Int], f: String => EitherT[List, String, Int]) => + eithert.leftFlatMap(f) should ===(eithert.swap.flatMap(a => f(a).swap).swap) + } + } + + test("leftSemiflatMap consistent with leftMap") { + forAll { (eithert: EitherT[List, String, Int], f: String => String) => + eithert.leftSemiflatMap(v => List(f(v))) should ===(eithert.leftMap(f)) + } + } + + test("leftSemiflatmap consistent with swap and the semiflatMap") { + forAll { (eithert: EitherT[List, String, Int], f: String => List[String]) => + eithert.leftSemiflatMap(f) should ===(eithert.swap.semiflatMap(a => f(a)).swap) + } + } + }