diff --git a/core/src/main/scala/cats/implicits.scala b/core/src/main/scala/cats/implicits.scala index f04f033c73..1267851ae6 100644 --- a/core/src/main/scala/cats/implicits.scala +++ b/core/src/main/scala/cats/implicits.scala @@ -6,6 +6,7 @@ object implicits with syntax.AllSyntaxBinCompat1 with syntax.AllSyntaxBinCompat2 with syntax.AllSyntaxBinCompat3 + with syntax.AllSyntaxBinCompat4 with instances.AllInstances with instances.AllInstancesBinCompat0 with instances.AllInstancesBinCompat1 diff --git a/core/src/main/scala/cats/syntax/all.scala b/core/src/main/scala/cats/syntax/all.scala index ec7fbec630..8a6785133b 100644 --- a/core/src/main/scala/cats/syntax/all.scala +++ b/core/src/main/scala/cats/syntax/all.scala @@ -7,6 +7,7 @@ abstract class AllSyntaxBinCompat with AllSyntaxBinCompat1 with AllSyntaxBinCompat2 with AllSyntaxBinCompat3 + with AllSyntaxBinCompat4 trait AllSyntax extends AlternativeSyntax @@ -77,3 +78,5 @@ trait AllSyntaxBinCompat2 with ValidatedSyntaxBincompat0 trait AllSyntaxBinCompat3 extends UnorderedFoldableSyntax with Function1Syntax + +trait AllSyntaxBinCompat4 extends TraverseFilterSyntaxBinCompat0 diff --git a/core/src/main/scala/cats/syntax/package.scala b/core/src/main/scala/cats/syntax/package.scala index 9ac1a92650..10fd9e2b31 100644 --- a/core/src/main/scala/cats/syntax/package.scala +++ b/core/src/main/scala/cats/syntax/package.scala @@ -51,7 +51,7 @@ package object syntax { object strong extends StrongSyntax object try_ extends TrySyntax object traverse extends TraverseSyntax - object traverseFilter extends TraverseFilterSyntax + object traverseFilter extends TraverseFilterSyntax with TraverseFilterSyntaxBinCompat0 object nonEmptyTraverse extends NonEmptyTraverseSyntax object unorderedFoldable extends UnorderedFoldableSyntax object unorderedTraverse extends UnorderedTraverseSyntax diff --git a/core/src/main/scala/cats/syntax/traverseFilter.scala b/core/src/main/scala/cats/syntax/traverseFilter.scala index 794caeddee..1763731702 100644 --- a/core/src/main/scala/cats/syntax/traverseFilter.scala +++ b/core/src/main/scala/cats/syntax/traverseFilter.scala @@ -2,3 +2,21 @@ package cats package syntax trait TraverseFilterSyntax extends TraverseFilter.ToTraverseFilterOps + +trait TraverseFilterSyntaxBinCompat0 { + implicit def toSequenceFilterOps[F[_], G[_], A](fgoa: F[G[Option[A]]]): SequenceFilterOps[F, G, A] = + new SequenceFilterOps(fgoa) +} + +final class SequenceFilterOps[F[_], G[_], A](val fgoa: F[G[Option[A]]]) extends AnyVal { + + /** + * {{{ + * scala> import cats.implicits._ + * scala> val a: List[Either[String, Option[Int]]] = List(Right(Some(1)), Right(Some(5)), Right(Some(3))) + * scala> val b: Either[String, List[Int]] = a.sequenceFilter + * b: Either[String, List[Int]] = Right(List(1, 5, 3)) + * }}} + * */ + def sequenceFilter(implicit F: TraverseFilter[F], G: Applicative[G]): G[F[A]] = F.traverseFilter(fgoa)(identity) +} diff --git a/tests/src/test/scala/cats/tests/SyntaxSuite.scala b/tests/src/test/scala/cats/tests/SyntaxSuite.scala index 30f28a867e..02f0377a0b 100644 --- a/tests/src/test/scala/cats/tests/SyntaxSuite.scala +++ b/tests/src/test/scala/cats/tests/SyntaxSuite.scala @@ -5,8 +5,8 @@ import scala.collection.immutable.SortedSet import scala.collection.immutable.SortedMap import cats.arrow.Compose import cats.data.{Binested, Nested, NonEmptyChain, NonEmptyList, NonEmptySet} -import cats.instances.AllInstances -import cats.syntax.{AllSyntax, AllSyntaxBinCompat} +import cats.instances.{AllInstances, AllInstancesBinCompat0, AllInstancesBinCompat1, AllInstancesBinCompat2} +import cats.syntax.AllSyntaxBinCompat /** * Test that our syntax implicits are working. @@ -26,7 +26,12 @@ import cats.syntax.{AllSyntax, AllSyntaxBinCompat} * * None of these tests should ever run, or do any runtime checks. */ -object SyntaxSuite extends AllSyntaxBinCompat with AllInstances with AllSyntax { +object SyntaxSuite + extends AllSyntaxBinCompat + with AllInstances + with AllInstancesBinCompat0 + with AllInstancesBinCompat1 + with AllInstancesBinCompat2 { // pretend we have a value of type A def mock[A]: A = ??? @@ -387,4 +392,9 @@ object SyntaxSuite extends AllSyntaxBinCompat with AllInstances with AllSyntax { val grouped: SortedMap[B, NonEmptyChain[A]] = list.groupByNec(f) } + def testSequenceFilter[A, B]: Unit = { + val f = mock[List[Either[A, Option[B]]]] + + val result: Either[A, List[B]] = f.sequenceFilter + } }