diff --git a/shared/src/main/scala/mouse/feither.scala b/shared/src/main/scala/mouse/feither.scala index e2f9f54..9aed308 100644 --- a/shared/src/main/scala/mouse/feither.scala +++ b/shared/src/main/scala/mouse/feither.scala @@ -54,6 +54,12 @@ final class FEitherOps[F[_], L, R](private val felr: F[Either[L, R]]) extends An case Right(value) => f(value) } + def flatTapIn[A >: L, B](f: R => Either[A, B])(implicit F: Functor[F]): F[Either[A, R]] = + flatMapIn(value => f(value).map(_ => value)) + + def flatTapF[A >: L, B](f: R => F[Either[A, B]])(implicit F: Monad[F]): F[Either[A, R]] = + flatMapF(value => F.map(f(value))(_.map(_ => value))) + def foldIn[A](left: L => A)(right: R => A)(implicit F: Functor[F]): F[A] = cata(left, right) diff --git a/shared/src/main/scala/mouse/foption.scala b/shared/src/main/scala/mouse/foption.scala index 781d0a7..44e0ebc 100644 --- a/shared/src/main/scala/mouse/foption.scala +++ b/shared/src/main/scala/mouse/foption.scala @@ -73,6 +73,12 @@ final class FOptionOps[F[_], A](private val foa: F[Option[A]]) extends AnyVal { def flatMapF[B](f: A => F[Option[B]])(implicit F: Monad[F]): F[Option[B]] = F.flatMap(foa)(_.fold(F.pure(Option.empty[B]))(f)) + def flatTapIn[B](f: A => Option[B])(implicit F: Functor[F]): F[Option[A]] = + flatMapIn(value => f(value).map(_ => value)) + + def flatTapF[B](f: A => F[Option[B]])(implicit F: Monad[F]): F[Option[A]] = + flatMapF { value => F.map(f(value))(_.map(_ => value)) } + def foldIn[B](default: => B)(f: A => B)(implicit F: Functor[F]): F[B] = cata(default, f) diff --git a/shared/src/test/scala/mouse/FEitherSyntaxTest.scala b/shared/src/test/scala/mouse/FEitherSyntaxTest.scala index 29b2144..e9bd616 100644 --- a/shared/src/test/scala/mouse/FEitherSyntaxTest.scala +++ b/shared/src/test/scala/mouse/FEitherSyntaxTest.scala @@ -57,6 +57,16 @@ class FEitherSyntaxTest extends MouseSuite { assertEquals(leftValue.flatMapF(i => List((i * 2).asRight[String])), leftValue) } + test("FEitherSyntax.flatTapIn") { + assertEquals(rightValue.flatTapIn(i => (i * 2).asRight[String]), List(42.asRight[String])) + assertEquals(leftValue.flatTapIn(i => (i * 2).asRight[String]), leftValue) + } + + test("FEitherSyntax.flatTapF") { + assertEquals(rightValue.flatTapF(i => List((i * 2).asRight[String])), List(42.asRight[String])) + assertEquals(leftValue.flatTapF(i => List((i * 2).asRight[String])), leftValue) + } + test("FEitherSyntax.foldIn") { assertEquals(rightValue.foldIn(_ => 0)(_ => 1), List(1)) assertEquals(leftValue.foldIn(_ => 0)(_ => 1), List(0)) diff --git a/shared/src/test/scala/mouse/FOptionSyntaxTest.scala b/shared/src/test/scala/mouse/FOptionSyntaxTest.scala index d640bb1..b80b102 100644 --- a/shared/src/test/scala/mouse/FOptionSyntaxTest.scala +++ b/shared/src/test/scala/mouse/FOptionSyntaxTest.scala @@ -72,6 +72,16 @@ class FOptionSyntaxTest extends MouseSuite { assertEquals(List(Option.empty[Int]).flatMapF(a => List(Option(a * 2))), List(Option.empty[Int])) } + test("FOptionSyntax.flatTapIn") { + assertEquals(List(Option(1)).flatTapIn(a => Option(a * 2)), List(Option(1))) + assertEquals(List(Option.empty[Int]).flatTapIn(a => Option(a * 2)), List(Option.empty[Int])) + } + + test("FOptionSyntax.flatMapF") { + assertEquals(List(Option(1)).flatTapF(a => List(Option(a * 2))), List(Option(1))) + assertEquals(List(Option.empty[Int]).flatTapF(a => List(Option(a * 2))), List(Option.empty[Int])) + } + test("FOptionSyntax.foldIn") { assertEquals(List(Option(1)).foldIn(false)(_ => true), List(true)) assertEquals(List(Option.empty[Int]).foldIn(false)(_ => true), List(false))