diff --git a/core/src/main/scala/cats/syntax/all.scala b/core/src/main/scala/cats/syntax/all.scala index 5a65ddeaf5..e087eed366 100644 --- a/core/src/main/scala/cats/syntax/all.scala +++ b/core/src/main/scala/cats/syntax/all.scala @@ -84,3 +84,4 @@ trait AllSyntaxBinCompat4 with ParallelApplySyntax with FoldableSyntaxBinCompat0 with ReducibleSyntaxBinCompat0 + with BitraverseSyntaxBinCompat0 diff --git a/core/src/main/scala/cats/syntax/bitraverse.scala b/core/src/main/scala/cats/syntax/bitraverse.scala index a21b474c50..ff3ee775b5 100644 --- a/core/src/main/scala/cats/syntax/bitraverse.scala +++ b/core/src/main/scala/cats/syntax/bitraverse.scala @@ -22,3 +22,64 @@ final class NestedBitraverseOps[F[_, _], G[_], A, B](private val fgagb: F[G[A], def bisequence(implicit F: Bitraverse[F], G: Applicative[G]): G[F[A, B]] = F.bisequence(fgagb) } + +trait BitraverseSyntaxBinCompat0 { + implicit final def catsSyntaxBitraverseBinCompat0[F[_, _]: Bitraverse, A, B]( + fab: F[A, B] + ): BitraverseOpsBinCompat0[F, A, B] = + new BitraverseOpsBinCompat0[F, A, B](fab) + implicit final def catsSyntaxLeftNestedBitraverse[F[_, _]: Bitraverse, G[_], A, B]( + fgab: F[G[A], B] + ): LeftNestedBitraverseOps[F, G, A, B] = + new LeftNestedBitraverseOps[F, G, A, B](fgab) +} + +final class BitraverseOpsBinCompat0[F[_, _], A, B](val fab: F[A, B]) extends AnyVal { + + /** + * Traverse over the left side of the structure. + * For the right side, use the standard `traverse` from [[cats.Traverse]]. + * + * Example: + * {{{ + * scala> import cats.implicits._ + * + * scala> val intAndString: (Int, String) = (7, "test") + * + * scala> intAndString.leftTraverse(i => Option(i).filter(_ > 5)) + * res1: Option[(Int, String)] = Some((7,test)) + * + * scala> intAndString.leftTraverse(i => Option(i).filter(_ < 5)) + * res2: Option[(Int, String)] = None + * }}} + */ + def leftTraverse[G[_], C](f: A => G[C])(implicit F: Bitraverse[F], G: Applicative[G]): G[F[C, B]] = + F.bitraverse(fab)(f, G.pure(_)) +} + +final class LeftNestedBitraverseOps[F[_, _], G[_], A, B](val fgab: F[G[A], B]) extends AnyVal { + + /** + * Sequence the left side of the structure. + * For the right side, use the standard `sequence` from [[cats.Traverse]]. + * + * Example: + * {{{ + * scala> import cats.implicits._ + * + * scala> val optionalErrorRight: Either[Option[String], Int] = Either.right(123) + * scala> optionalErrorRight.leftSequence + * res1: Option[Either[String, Int]] = Some(Right(123)) + * + * scala> val optionalErrorLeftSome: Either[Option[String], Int] = Either.left(Some("something went wrong")) + * scala> optionalErrorLeftSome.leftSequence + * res2: Option[Either[String, Int]] = Some(Left(something went wrong)) + * + * scala> val optionalErrorLeftNone: Either[Option[String], Int] = Either.left(None) + * scala> optionalErrorLeftNone.leftSequence + * res3: Option[Either[String,Int]] = None + * }}} + */ + def leftSequence(implicit F: Bitraverse[F], G: Applicative[G]): G[F[A, B]] = + F.bitraverse(fgab)(identity, G.pure(_)) +} diff --git a/core/src/main/scala/cats/syntax/package.scala b/core/src/main/scala/cats/syntax/package.scala index a4f02e5596..b883817ce0 100644 --- a/core/src/main/scala/cats/syntax/package.scala +++ b/core/src/main/scala/cats/syntax/package.scala @@ -11,7 +11,7 @@ package object syntax { object bifunctor extends BifunctorSyntax object bifoldable extends BifoldableSyntax object binested extends BinestedSyntax - object bitraverse extends BitraverseSyntax + object bitraverse extends BitraverseSyntax with BitraverseSyntaxBinCompat0 @deprecated("use cats.syntax.semigroupal instead", "1.0.0-RC1") object cartesian extends SemigroupalSyntax object choice extends ChoiceSyntax diff --git a/tests/src/test/scala/cats/tests/SyntaxSuite.scala b/tests/src/test/scala/cats/tests/SyntaxSuite.scala index b309034fae..74388d57d5 100644 --- a/tests/src/test/scala/cats/tests/SyntaxSuite.scala +++ b/tests/src/test/scala/cats/tests/SyntaxSuite.scala @@ -307,9 +307,13 @@ object SyntaxSuite val fab = mock[F[A, B]] val gfcd = fab.bitraverse(f, g) + val gfcb = fab.leftTraverse(f) val fgagb = mock[F[G[A], G[B]]] val gfab = fgagb.bisequence + + val fgab = mock[F[G[A], B]] + val gfab2 = fgab.leftSequence } def testAlternativeMonad[F[_]: Alternative: Monad, G[_]: Foldable, H[_, _]: Bifoldable, A, B]: Unit = {