diff --git a/core/src/main/scala/cats/syntax/all.scala b/core/src/main/scala/cats/syntax/all.scala index dec09ed8e0..87c2497f04 100644 --- a/core/src/main/scala/cats/syntax/all.scala +++ b/core/src/main/scala/cats/syntax/all.scala @@ -59,4 +59,6 @@ trait AllSyntaxBinCompat0 with ApplicativeErrorExtension with TrySyntax -trait AllSyntaxBinCompat1 extends FlatMapOptionSyntax +trait AllSyntaxBinCompat1 + extends FlatMapOptionSyntax + with NestedSyntax diff --git a/core/src/main/scala/cats/syntax/nested.scala b/core/src/main/scala/cats/syntax/nested.scala new file mode 100644 index 0000000000..c31af329d2 --- /dev/null +++ b/core/src/main/scala/cats/syntax/nested.scala @@ -0,0 +1,27 @@ +package cats +package syntax + +import cats.data.Nested + +trait NestedSyntax { + implicit final def catsSyntaxNestedId[F[_], G[_], A](value: F[G[A]]): NestedIdOps[F, G, A] = + new NestedIdOps[F, G, A](value) +} + +final class NestedIdOps[F[_], G[_], A](val value: F[G[A]]) extends AnyVal { + /** + * Wrap a value in `Nested`. + * + * `x.nested` is equivalent to `Nested(x)`. + * + * Example: + * {{{ + * scala> import cats.implicits._ + * scala> List(Some(3), None).nested.map(_+1).value + * res0: List[Option[Int]] = List(Some(4), None) + * }}} + */ + def nested: Nested[F, G, A] = Nested[F, G, A](value) +} + + diff --git a/core/src/main/scala/cats/syntax/package.scala b/core/src/main/scala/cats/syntax/package.scala index b928818bc9..e83d5cdcf9 100644 --- a/core/src/main/scala/cats/syntax/package.scala +++ b/core/src/main/scala/cats/syntax/package.scala @@ -33,6 +33,7 @@ package object syntax { object monad extends MonadSyntax object monadError extends MonadErrorSyntax object monoid extends MonoidSyntax + object nested extends NestedSyntax object option extends OptionSyntax object order extends OrderSyntax object parallel extends ParallelSyntax diff --git a/tests/src/test/scala/cats/tests/SyntaxSuite.scala b/tests/src/test/scala/cats/tests/SyntaxSuite.scala index 6bcd3b987b..5dc48aa6c0 100644 --- a/tests/src/test/scala/cats/tests/SyntaxSuite.scala +++ b/tests/src/test/scala/cats/tests/SyntaxSuite.scala @@ -2,8 +2,9 @@ package cats package tests import cats.arrow.Compose +import cats.data.Nested import cats.instances.AllInstances -import cats.syntax.AllSyntax +import cats.syntax.{AllSyntax, AllSyntaxBinCompat1} /** @@ -24,7 +25,7 @@ import cats.syntax.AllSyntax * * None of these tests should ever run, or do any runtime checks. */ -object SyntaxSuite extends AllInstances with AllSyntax { +object SyntaxSuite extends AllInstances with AllSyntax with AllSyntaxBinCompat1 { // pretend we have a value of type A def mock[A]: A = ??? @@ -338,5 +339,11 @@ object SyntaxSuite extends AllInstances with AllSyntax { val gea4 = ga.recoverWith(pfegea) } + def testNested[F[_], G[_], A]: Unit = { + val fga: F[G[A]] = mock[F[G[A]]] + + val nested: Nested[F, G, A] = fga.nested + } + }