Skip to content

Commit

Permalink
Add traverseEither (#3294)
Browse files Browse the repository at this point in the history
  • Loading branch information
LukaJCB authored Feb 14, 2020
1 parent 86d2c8b commit 28143e7
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 1 deletion.
13 changes: 13 additions & 0 deletions core/src/main/scala/cats/TraverseFilter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,19 @@ trait TraverseFilter[F[_]] extends FunctorFilter[F] {
def filterA[G[_], A](fa: F[A])(f: A => G[Boolean])(implicit G: Applicative[G]): G[F[A]] =
traverseFilter(fa)(a => G.map(f(a))(if (_) Some(a) else None))

/**
* Like [[traverseFilter]], but uses `Either` instead of `Option` and allows for an action to be run on each filtered value.
*/
def traverseEither[G[_], A, B, E](
fa: F[A]
)(f: A => G[Either[E, B]])(g: (A, E) => G[Unit])(implicit G: Monad[G]): G[F[B]] =
traverseFilter(fa)(a =>
G.flatMap(f(a)) {
case Left(e) => G.as(g(a, e), Option.empty[B])
case Right(b) => G.pure(Some(b))
}
)

override def mapFilter[A, B](fa: F[A])(f: A => Option[B]): F[B] =
traverseFilter[Id, A, B](fa)(f)
}
5 changes: 5 additions & 0 deletions laws/src/main/scala/cats/laws/TraverseFilterLaws.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ trait TraverseFilterLaws[F[_]] extends FunctorFilterLaws[F] {

def filterAConsistentWithTraverseFilter[G[_]: Applicative, A](fa: F[A], f: A => G[Boolean]): IsEq[G[F[A]]] =
fa.filterA(f) <-> fa.traverseFilter(a => f(a).map(if (_) Some(a) else None))

def traverseEitherConsistentWithTraverseFilter[G[_], E, A, B](fa: F[A], f: A => G[Option[B]], e: E)(
implicit G: Monad[G]
): IsEq[G[F[B]]] =
fa.traverseEither(a => f(a).map(_.toRight(e)))((_, _) => Applicative[G].unit) <-> fa.traverseFilter(f)
}

object TraverseFilterLaws {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ trait TraverseFilterTests[F[_]] extends FunctorFilterTests[F] {
"traverseFilter identity" -> forAll(laws.traverseFilterIdentity[Option, A] _),
"traverseFilter nested composition" -> forAll(laws.traverseFilterComposition[A, B, C, Option, Option] _),
"traverseFilter consistent with traverse" -> forAll(laws.traverseFilterConsistentWithTraverse[Option, A] _),
"filterA consistent with traverseFilter" -> forAll(laws.filterAConsistentWithTraverseFilter[Option, A] _)
"filterA consistent with traverseFilter" -> forAll(laws.filterAConsistentWithTraverseFilter[Option, A] _),
"traverseEither consistent with traverseFilter" -> forAll(
laws.traverseEitherConsistentWithTraverseFilter[Option, F[A], A, B] _
)
)
}

Expand Down

0 comments on commit 28143e7

Please sign in to comment.