Skip to content

Commit

Permalink
Merge pull request #501 from danicheg/or-keep
Browse files Browse the repository at this point in the history
Add `mapOrKeepIn` / `flatMapOrKeepIn` to `F[Option[A]]` and `F[Either[A,B]]` syntaxes
  • Loading branch information
danicheg committed Jun 23, 2024
2 parents 772f5e0 + 44806a0 commit 502f33d
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 0 deletions.
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

0 comments on commit 502f33d

Please sign in to comment.