Skip to content

Commit

Permalink
Add semiflatTap and leftSemiflatTap functions to EitherT (typelevel#3316
Browse files Browse the repository at this point in the history
)

* Add semiflatTap and leftSemiflatTap functions to EitherT

* Replaces Monad[F] with F
  • Loading branch information
matwojcik authored Feb 27, 2020
1 parent 23de622 commit 25e840d
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 3 deletions.
6 changes: 6 additions & 0 deletions core/src/main/scala/cats/data/EitherT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,12 @@ final case class EitherT[F[_], A, B](value: F[Either[A, B]]) {
}
})

def semiflatTap[C](f: B => F[C])(implicit F: Monad[F]): EitherT[F, A, B] =
semiflatMap(b => F.as(f(b), b))

def leftSemiflatTap[C](f: A => F[C])(implicit F: Monad[F]): EitherT[F, A, B] =
leftSemiflatMap(a => F.as(f(a), a))

def compare(that: EitherT[F, A, B])(implicit o: Order[F[Either[A, B]]]): Int =
o.compare(value, that.value)

Expand Down
42 changes: 39 additions & 3 deletions tests/src/test/scala/cats/tests/EitherTSuite.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package cats
package tests

import cats.Bifunctor
import cats.data.EitherT

import cats.data.{EitherT, State}
import cats.laws.discipline._
import cats.laws.discipline.arbitrary._
import cats.laws.discipline.SemigroupalTests.Isomorphisms
import cats.kernel.laws.discipline.{EqTests, MonoidTests, OrderTests, PartialOrderTests, SemigroupTests}

import scala.util.{Failure, Success, Try}

class EitherTSuite extends CatsSuite {
Expand Down Expand Up @@ -541,6 +540,43 @@ class EitherTSuite extends CatsSuite {
}
}

test("semiflatTap does not change the return value") {
type TestEffect[A] = State[List[Int], A]
forAll { (eithert: EitherT[TestEffect, String, Int], f: Int => TestEffect[Int], initial: List[Int]) =>
eithert.semiflatTap(v => f(v)).value.runA(initial) should ===(eithert.value.runA(initial))
}
}

test("semiflatTap runs the effect") {
type TestEffect[A] = State[List[Int], A]
forAll { (eithert: EitherT[TestEffect, String, Int], f: Int => TestEffect[Int], initial: List[Int]) =>
eithert.semiflatTap(v => f(v)).value.runS(initial) should ===(eithert.semiflatMap(f).value.runS(initial))
}
}

test("leftSemiflatTap does not change the return value") {
type TestEffect[A] = State[List[Int], A]
forAll { (eithert: EitherT[TestEffect, String, Int], f: String => TestEffect[Int], initial: List[Int]) =>
eithert.leftSemiflatTap(v => f(v)).value.runA(initial) should ===(eithert.value.runA(initial))
}
}

test("leftSemiflatTap runs the effect") {
type TestEffect[A] = State[List[Int], A]
forAll { (eithert: EitherT[TestEffect, String, Int], f: String => TestEffect[Int], initial: List[Int]) =>
eithert.leftSemiflatTap(v => f(v)).value.runS(initial) should ===(eithert.leftSemiflatMap(f).value.runS(initial))
}
}

test("leftSemiflatTap consistent with swap and the semiflatTap") {
type TestEffect[A] = State[List[Int], A]
forAll { (eithert: EitherT[TestEffect, String, Int], f: String => TestEffect[Int], initial: List[Int]) =>
eithert.leftSemiflatTap(v => f(v)).value.runA(initial) should ===(
eithert.swap.semiflatTap(v => f(v)).swap.value.runA(initial)
)
}
}

test("biSemiflatMap consistent with leftSemiflatMap and semiFlatmap") {
forAll { (eithert: EitherT[List, String, Int], fa: String => List[Int], fb: Int => List[String]) =>
eithert.biSemiflatMap(fa, fb) should ===(eithert.leftSemiflatMap(fa).semiflatMap(fb))
Expand Down

0 comments on commit 25e840d

Please sign in to comment.