From cd3840613cb1a6cdf8d56f61095c6d4a7a247f3b Mon Sep 17 00:00:00 2001 From: vendethiel Date: Sun, 6 Aug 2017 11:45:04 +0200 Subject: [PATCH 1/3] Implement EitherT#leftFlatMap and EitherT#leftSemiflatMap --- core/src/main/scala/cats/data/EitherT.scala | 12 ++++++++++++ tests/src/test/scala/cats/tests/EitherTTests.scala | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/core/src/main/scala/cats/data/EitherT.scala b/core/src/main/scala/cats/data/EitherT.scala index 54120d43fe..6cac188086 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..692e9d7411 100644 --- a/tests/src/test/scala/cats/tests/EitherTTests.scala +++ b/tests/src/test/scala/cats/tests/EitherTTests.scala @@ -434,4 +434,16 @@ class EitherTTests extends CatsSuite { } yield s1 ++ s2 } + test("leftFlatMap") { + forAll { (eithert: EitherT[List, String, Int], f: String => String) => + eithert.leftFlatMap(v => EitherT.left[Int](List(f(v)))) should ===(eithert.leftMap(f)) + } + } + + test("leftSemiflatMap") { + forAll { (eithert: EitherT[List, String, Int], f: String => String) => + eithert.leftSemiflatMap(v => List(f(v))) should ===(eithert.leftMap(f)) + } + } + } From 9c27cf3cfe9860bc516d2e0528581ac22b61ee9a Mon Sep 17 00:00:00 2001 From: karpin_r Date: Tue, 26 Sep 2017 10:40:52 +0200 Subject: [PATCH 2/3] Add additional tests for EitherT --- tests/src/test/scala/cats/tests/EitherTTests.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/src/test/scala/cats/tests/EitherTTests.scala b/tests/src/test/scala/cats/tests/EitherTTests.scala index 692e9d7411..658abfe8da 100644 --- a/tests/src/test/scala/cats/tests/EitherTTests.scala +++ b/tests/src/test/scala/cats/tests/EitherTTests.scala @@ -438,12 +438,18 @@ class EitherTTests extends CatsSuite { forAll { (eithert: EitherT[List, String, Int], f: String => String) => eithert.leftFlatMap(v => EitherT.left[Int](List(f(v)))) should ===(eithert.leftMap(f)) } + 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") { forAll { (eithert: EitherT[List, String, Int], f: String => String) => eithert.leftSemiflatMap(v => List(f(v))) should ===(eithert.leftMap(f)) } + forAll { (eithert: EitherT[List, String, Int], f: String => List[String]) => + eithert.leftSemiflatMap(f) should ===(eithert.swap.semiflatMap(a => f(a)).swap) + } } } From edef90880e8d95ac9cba753f7bc0ef3d94dfd07d Mon Sep 17 00:00:00 2001 From: LukaJCB Date: Fri, 29 Sep 2017 10:17:17 +0200 Subject: [PATCH 3/3] Rename tests Tiny nit --- tests/src/test/scala/cats/tests/EitherTTests.scala | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/src/test/scala/cats/tests/EitherTTests.scala b/tests/src/test/scala/cats/tests/EitherTTests.scala index 658abfe8da..3ae21c74e5 100644 --- a/tests/src/test/scala/cats/tests/EitherTTests.scala +++ b/tests/src/test/scala/cats/tests/EitherTTests.scala @@ -434,19 +434,25 @@ class EitherTTests extends CatsSuite { } yield s1 ++ s2 } - test("leftFlatMap") { + 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") { + 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) }