Skip to content

Commit 25e840d

Browse files
authored
Add semiflatTap and leftSemiflatTap functions to EitherT (typelevel#3316)
* Add semiflatTap and leftSemiflatTap functions to EitherT * Replaces Monad[F] with F
1 parent 23de622 commit 25e840d

File tree

2 files changed

+45
-3
lines changed

2 files changed

+45
-3
lines changed

core/src/main/scala/cats/data/EitherT.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,12 @@ final case class EitherT[F[_], A, B](value: F[Either[A, B]]) {
447447
}
448448
})
449449

450+
def semiflatTap[C](f: B => F[C])(implicit F: Monad[F]): EitherT[F, A, B] =
451+
semiflatMap(b => F.as(f(b), b))
452+
453+
def leftSemiflatTap[C](f: A => F[C])(implicit F: Monad[F]): EitherT[F, A, B] =
454+
leftSemiflatMap(a => F.as(f(a), a))
455+
450456
def compare(that: EitherT[F, A, B])(implicit o: Order[F[Either[A, B]]]): Int =
451457
o.compare(value, that.value)
452458

tests/src/test/scala/cats/tests/EitherTSuite.scala

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
package cats
22
package tests
33

4-
import cats.Bifunctor
5-
import cats.data.EitherT
6-
4+
import cats.data.{EitherT, State}
75
import cats.laws.discipline._
86
import cats.laws.discipline.arbitrary._
97
import cats.laws.discipline.SemigroupalTests.Isomorphisms
108
import cats.kernel.laws.discipline.{EqTests, MonoidTests, OrderTests, PartialOrderTests, SemigroupTests}
9+
1110
import scala.util.{Failure, Success, Try}
1211

1312
class EitherTSuite extends CatsSuite {
@@ -541,6 +540,43 @@ class EitherTSuite extends CatsSuite {
541540
}
542541
}
543542

543+
test("semiflatTap does not change the return value") {
544+
type TestEffect[A] = State[List[Int], A]
545+
forAll { (eithert: EitherT[TestEffect, String, Int], f: Int => TestEffect[Int], initial: List[Int]) =>
546+
eithert.semiflatTap(v => f(v)).value.runA(initial) should ===(eithert.value.runA(initial))
547+
}
548+
}
549+
550+
test("semiflatTap runs the effect") {
551+
type TestEffect[A] = State[List[Int], A]
552+
forAll { (eithert: EitherT[TestEffect, String, Int], f: Int => TestEffect[Int], initial: List[Int]) =>
553+
eithert.semiflatTap(v => f(v)).value.runS(initial) should ===(eithert.semiflatMap(f).value.runS(initial))
554+
}
555+
}
556+
557+
test("leftSemiflatTap does not change the return value") {
558+
type TestEffect[A] = State[List[Int], A]
559+
forAll { (eithert: EitherT[TestEffect, String, Int], f: String => TestEffect[Int], initial: List[Int]) =>
560+
eithert.leftSemiflatTap(v => f(v)).value.runA(initial) should ===(eithert.value.runA(initial))
561+
}
562+
}
563+
564+
test("leftSemiflatTap runs the effect") {
565+
type TestEffect[A] = State[List[Int], A]
566+
forAll { (eithert: EitherT[TestEffect, String, Int], f: String => TestEffect[Int], initial: List[Int]) =>
567+
eithert.leftSemiflatTap(v => f(v)).value.runS(initial) should ===(eithert.leftSemiflatMap(f).value.runS(initial))
568+
}
569+
}
570+
571+
test("leftSemiflatTap consistent with swap and the semiflatTap") {
572+
type TestEffect[A] = State[List[Int], A]
573+
forAll { (eithert: EitherT[TestEffect, String, Int], f: String => TestEffect[Int], initial: List[Int]) =>
574+
eithert.leftSemiflatTap(v => f(v)).value.runA(initial) should ===(
575+
eithert.swap.semiflatTap(v => f(v)).swap.value.runA(initial)
576+
)
577+
}
578+
}
579+
544580
test("biSemiflatMap consistent with leftSemiflatMap and semiFlatmap") {
545581
forAll { (eithert: EitherT[List, String, Int], fa: String => List[Int], fb: Int => List[String]) =>
546582
eithert.biSemiflatMap(fa, fb) should ===(eithert.leftSemiflatMap(fa).semiflatMap(fb))

0 commit comments

Comments
 (0)