Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mapOrKeepIn / flatMapOrKeepIn to F[Option[A]] and F[Either[A,B]] syntaxes #501

Merged
merged 9 commits into from
Jun 23, 2024
6 changes: 6 additions & 0 deletions shared/src/main/scala/mouse/feither.scala
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ final class FEitherOps[F[_], L, R](private val felr: F[Either[L, R]]) extends An
def flatMapIn[A >: L, B](f: R => Either[A, B])(implicit F: Functor[F]): F[Either[A, B]] =
F.map(felr)(_.flatMap(f))

def flatMapOrKeepIn[A >: L, B >: R](pfa: PartialFunction[R, Either[A, B]])(implicit F: Functor[F]): F[Either[A, B]] =
F.map(felr)(_.flatMap(a => pfa.applyOrElse(a, Right[A, B](_))))

def flatMapF[A >: L, B](f: R => F[Either[A, B]])(implicit F: Monad[F]): F[Either[A, B]] =
F.flatMap(felr) {
case l @ Left(_) => F.pure(l.asInstanceOf[Left[A, B]])
Expand Down Expand Up @@ -117,6 +120,9 @@ final class FEitherOps[F[_], L, R](private val felr: F[Either[L, R]]) extends An
def mapIn[A](f: R => A)(implicit F: Functor[F]): F[Either[L, A]] =
F.map(felr)(_.map(f))

def mapOrKeepIn[A >: R](pf: PartialFunction[R, A])(implicit F: Functor[F]): F[Either[L, A]] =
F.map(felr)(_.map(a => pf.applyOrElse(a, identity[A])))

def asIn[B](b: => B)(implicit F: Functor[F]): F[Either[L, B]] =
mapIn(_ => b)

Expand Down
6 changes: 6 additions & 0 deletions shared/src/main/scala/mouse/foption.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ final class FOptionOps[F[_], A](private val foa: F[Option[A]]) extends AnyVal {
def flatMapIn[B](f: A => Option[B])(implicit F: Functor[F]): F[Option[B]] =
F.map(foa)(_.flatMap(f))

def flatMapOrKeepIn[B >: A](pfa: PartialFunction[A, Option[B]])(implicit F: Functor[F]): F[Option[B]] =
F.map(foa)(_.flatMap(a => pfa.applyOrElse(a, Option[B](_))))

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))

Expand Down Expand Up @@ -103,6 +106,9 @@ final class FOptionOps[F[_], A](private val foa: F[Option[A]]) extends AnyVal {
def mapIn[B](f: A => B)(implicit F: Functor[F]): F[Option[B]] =
F.map(foa)(_.map(f))

def mapOrKeepIn[B >: A](pf: PartialFunction[A, B])(implicit F: Functor[F]): F[Option[B]] =
F.map(foa)(_.map(a => pf.applyOrElse(a, identity[B])))

def asIn[B](b: => B)(implicit F: Functor[F]): F[Option[B]] =
mapIn(_ => b)

Expand Down
12 changes: 12 additions & 0 deletions shared/src/test/scala/mouse/FEitherSyntaxTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ class FEitherSyntaxTest extends MouseSuite {
assertEquals(leftValue.flatMapIn(i => (i * 2).asRight[String]), leftValue)
}

test("FEitherSyntax.flatMapOrKeepIn") {
assertEquals(rightValue.flatMapOrKeepIn { case 42 => 84.asRight[String] }, List(84.asRight[String]))
assertEquals(rightValue.flatMapOrKeepIn { case 84 => 42.asRight[String] }, rightValue)
assertEquals(leftValue.flatMapOrKeepIn { case 42 => 84.asRight[String] }, leftValue)
}

test("FEitherSyntax.leftWidenIn") {
val initial: Option[Either[List[Int], String]] = Option(List(1).asLeft[String])
val expected: Option[Either[Seq[Int], String]] = Option(Seq(1).asLeft[String])
Expand Down Expand Up @@ -133,6 +139,12 @@ class FEitherSyntaxTest extends MouseSuite {
assertEquals(leftValue.mapIn(_ * 2), leftValue)
}

test("FEitherSyntax.mapOrKeepIn") {
assertEquals(rightValue.mapOrKeepIn { case 42 => 84 }, List(84.asRight[String]))
assertEquals(rightValue.mapOrKeepIn { case 84 => 42 }, rightValue)
assertEquals(leftValue.mapOrKeepIn { case 42 => 84 }, leftValue)
}

test("FEitherSyntax.asIn") {
assertEquals(rightValue.asIn(2), List(2.asRight[String]))
assertEquals(leftValue.asIn(2), leftValue)
Expand Down
12 changes: 12 additions & 0 deletions shared/src/test/scala/mouse/FOptionSyntaxTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ class FOptionSyntaxTest extends MouseSuite {
assertEquals(List(Option.empty[Int]).flatMapIn(a => Option(a * 2)), List(Option.empty[Int]))
}

test("FOptionSyntax.flatMapOrKeepIn") {
assertEquals(List(Option(1)).flatMapOrKeepIn { case 1 => Option(2) }, List(Option(2)))
assertEquals(List(Option(1)).flatMapOrKeepIn { case 2 => Option(1) }, List(Option(1)))
assertEquals(List(Option.empty[Int]).flatMapOrKeepIn { case 1 => Option(2) }, List(Option.empty[Int]))
}

test("FOptionSyntax.flatMapF") {
assertEquals(List(Option(1)).flatMapF(a => List(Option(a * 2))), List(Option(2)))
assertEquals(List(Option.empty[Int]).flatMapF(a => List(Option(a * 2))), List(Option.empty[Int]))
Expand Down Expand Up @@ -125,6 +131,12 @@ class FOptionSyntaxTest extends MouseSuite {
assertEquals(List(Option.empty[Int]).mapIn(_ + 1), List(Option.empty[Int]))
}

test("FOptionSyntax.mapOrKeepIn") {
assertEquals(List(Option(1)).mapOrKeepIn { case 1 => 2 }, List(Option(2)))
assertEquals(List(Option(1)).mapOrKeepIn { case 2 => 1 }, List(Option(1)))
assertEquals(List(Option.empty[Int]).mapOrKeepIn { case 1 => 2 }, List(Option.empty[Int]))
}

test("FOptionSyntax.asIn") {
assertEquals(List(Option(1)).asIn(1), List(Option(1)))
assertEquals(List(Option.empty[Int]).asIn(1), List(Option.empty[Int]))
Expand Down