From 03b0fd534d03eabb49498278081e37a1c39937a5 Mon Sep 17 00:00:00 2001 From: catostrophe <40268503+catostrophe@users.noreply.github.com> Date: Fri, 21 May 2021 19:12:29 +0300 Subject: [PATCH 1/9] keep fa call-by-name in whenA/unlessA syntax extensions --- core/src/main/scala/cats/syntax/applicative.scala | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/src/main/scala/cats/syntax/applicative.scala b/core/src/main/scala/cats/syntax/applicative.scala index 4e2a271b02..abb142cb2a 100644 --- a/core/src/main/scala/cats/syntax/applicative.scala +++ b/core/src/main/scala/cats/syntax/applicative.scala @@ -6,6 +6,8 @@ trait ApplicativeSyntax { new ApplicativeIdOps[A](a) implicit final def catsSyntaxApplicative[F[_], A](fa: F[A]): ApplicativeOps[F, A] = new ApplicativeOps[F, A](fa) + implicit final def catsSyntaxApplicativeByName[F[_], A](fa: => F[A]): ApplicativeByNameOps[F, A] = + new ApplicativeByNameOps[F, A](() => fa) } final class ApplicativeIdOps[A](private val a: A) extends AnyVal { @@ -17,3 +19,8 @@ final class ApplicativeOps[F[_], A](private val fa: F[A]) extends AnyVal { def unlessA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.unlessA(cond)(fa) def whenA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.whenA(cond)(fa) } + +final class ApplicativeByNameOps[F[_], A](private val fa: () => F[A]) extends AnyVal { + def unlessA_(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.unlessA(cond)(fa()) + def whenA_(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.whenA(cond)(fa()) +} From d42c2fbbc742a63714037af60b52686793dc0cbc Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Tue, 17 May 2022 03:40:52 +0000 Subject: [PATCH 2/9] Make applicative ops by-name --- core/src/main/scala/cats/syntax/applicative.scala | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/core/src/main/scala/cats/syntax/applicative.scala b/core/src/main/scala/cats/syntax/applicative.scala index f5c2c9a44a..4bb5b52fba 100644 --- a/core/src/main/scala/cats/syntax/applicative.scala +++ b/core/src/main/scala/cats/syntax/applicative.scala @@ -25,16 +25,19 @@ package syntax trait ApplicativeSyntax { implicit final def catsSyntaxApplicativeId[A](a: A): ApplicativeIdOps[A] = new ApplicativeIdOps[A](a) - implicit final def catsSyntaxApplicative[F[_], A](fa: F[A]): ApplicativeOps[F, A] = - new ApplicativeOps[F, A](fa) + implicit final def catsSyntaxApplicative[F[_], A](fa: => F[A]): ApplicativeOps[F, A] = + new ApplicativeOps[F, A](() => fa) + @deprecated("Retained for bincompat", "2.8.0") + private[syntax] def catsSyntaxApplicative[F[_], A](fa: F[A]): ApplicativeOps[F, A] = + new ApplicativeOps[F, A](() => fa) } final class ApplicativeIdOps[A](private val a: A) extends AnyVal { def pure[F[_]](implicit F: Applicative[F]): F[A] = F.pure(a) } -final class ApplicativeOps[F[_], A](private val fa: F[A]) extends AnyVal { - def replicateA(n: Int)(implicit F: Applicative[F]): F[List[A]] = F.replicateA(n, fa) - def unlessA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.unlessA(cond)(fa) - def whenA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.whenA(cond)(fa) +final class ApplicativeOps[F[_], A](private val fa: () => F[A]) extends AnyVal { + def replicateA(n: Int)(implicit F: Applicative[F]): F[List[A]] = F.replicateA(n, fa()) + def unlessA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.unlessA(cond)(fa()) + def whenA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.whenA(cond)(fa()) } From 858cff4e620c1bbdebc0ef350a467ebd41c3447e Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Tue, 17 May 2022 03:45:10 +0000 Subject: [PATCH 3/9] Take 2, emphasizing bincompat --- .../main/scala/cats/syntax/applicative.scala | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/core/src/main/scala/cats/syntax/applicative.scala b/core/src/main/scala/cats/syntax/applicative.scala index 4bb5b52fba..4d70f3f43a 100644 --- a/core/src/main/scala/cats/syntax/applicative.scala +++ b/core/src/main/scala/cats/syntax/applicative.scala @@ -25,18 +25,25 @@ package syntax trait ApplicativeSyntax { implicit final def catsSyntaxApplicativeId[A](a: A): ApplicativeIdOps[A] = new ApplicativeIdOps[A](a) - implicit final def catsSyntaxApplicative[F[_], A](fa: => F[A]): ApplicativeOps[F, A] = - new ApplicativeOps[F, A](() => fa) - @deprecated("Retained for bincompat", "2.8.0") - private[syntax] def catsSyntaxApplicative[F[_], A](fa: F[A]): ApplicativeOps[F, A] = - new ApplicativeOps[F, A](() => fa) + implicit final def catsSyntaxApplicativeByName[F[_], A](fa: => F[A]): ApplicativeOpsByName[F, A] = + new ApplicativeOpsByName[F, A](() => fa) + @deprecated("Use by-name version", "2.8.0") + def catsSyntaxApplicative[F[_], A](fa: F[A]): ApplicativeOps[F, A] = + new ApplicativeOps[F, A](fa) } final class ApplicativeIdOps[A](private val a: A) extends AnyVal { def pure[F[_]](implicit F: Applicative[F]): F[A] = F.pure(a) } -final class ApplicativeOps[F[_], A](private val fa: () => F[A]) extends AnyVal { +@deprecated("Use by-name version", "2.8.0") +final class ApplicativeOps[F[_], A](private val fa: F[A]) extends AnyVal { + def replicateA(n: Int)(implicit F: Applicative[F]): F[List[A]] = F.replicateA(n, fa) + def unlessA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.unlessA(cond)(fa) + def whenA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.whenA(cond)(fa) +} + +final class ApplicativeOpsByName[F[_], A](private val fa: () => F[A]) extends AnyVal { def replicateA(n: Int)(implicit F: Applicative[F]): F[List[A]] = F.replicateA(n, fa()) def unlessA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.unlessA(cond)(fa()) def whenA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.whenA(cond)(fa()) From 2a671efafa4ce40e6e1606e1b89e2ae869eb0dd3 Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Tue, 17 May 2022 04:00:07 +0000 Subject: [PATCH 4/9] Renaming, fixing --- core/src/main/scala/cats/syntax/applicative.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/scala/cats/syntax/applicative.scala b/core/src/main/scala/cats/syntax/applicative.scala index 4d70f3f43a..78a202153e 100644 --- a/core/src/main/scala/cats/syntax/applicative.scala +++ b/core/src/main/scala/cats/syntax/applicative.scala @@ -25,10 +25,10 @@ package syntax trait ApplicativeSyntax { implicit final def catsSyntaxApplicativeId[A](a: A): ApplicativeIdOps[A] = new ApplicativeIdOps[A](a) - implicit final def catsSyntaxApplicativeByName[F[_], A](fa: => F[A]): ApplicativeOpsByName[F, A] = - new ApplicativeOpsByName[F, A](() => fa) + implicit final def catsSyntaxApplicativeByName[F[_], A](fa: => F[A]): ApplicativeByNameOps[F, A] = + new ApplicativeByNameOps[F, A](() => fa) @deprecated("Use by-name version", "2.8.0") - def catsSyntaxApplicative[F[_], A](fa: F[A]): ApplicativeOps[F, A] = + final def catsSyntaxApplicative[F[_], A](fa: F[A]): ApplicativeOps[F, A] = new ApplicativeOps[F, A](fa) } @@ -43,7 +43,7 @@ final class ApplicativeOps[F[_], A](private val fa: F[A]) extends AnyVal { def whenA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.whenA(cond)(fa) } -final class ApplicativeOpsByName[F[_], A](private val fa: () => F[A]) extends AnyVal { +final class ApplicativeByNameOps[F[_], A](private val fa: () => F[A]) extends AnyVal { def replicateA(n: Int)(implicit F: Applicative[F]): F[List[A]] = F.replicateA(n, fa()) def unlessA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.unlessA(cond)(fa()) def whenA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.whenA(cond)(fa()) From 40157f3b7f774b88d11ef30ffdc50f337c1f398b Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Tue, 17 May 2022 04:02:48 +0000 Subject: [PATCH 5/9] Add tests --- .../src/test/scala/cats/tests/ApplicativeSuite.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/shared/src/test/scala/cats/tests/ApplicativeSuite.scala b/tests/shared/src/test/scala/cats/tests/ApplicativeSuite.scala index b71625bd5b..2ce0dac7f7 100644 --- a/tests/shared/src/test/scala/cats/tests/ApplicativeSuite.scala +++ b/tests/shared/src/test/scala/cats/tests/ApplicativeSuite.scala @@ -63,6 +63,16 @@ class ApplicativeSuite extends CatsSuite { } } + test("by-name ops are lazy") { + var i = 0 + Option(i += 1).whenA(false) + assertEquals(i, 0) + + var j = 0 + Option(j += 1).unlessA(true) + assertEquals(j, 0) + } + { implicit val optionMonoid: Monoid[Option[Int]] = Applicative.monoid[Option, Int] checkAll("Applicative[Option].monoid", MonoidTests[Option[Int]](optionMonoid).monoid) From 3e8eabad140c2b768f86fc411f9dc3b21dc5ab32 Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Tue, 17 May 2022 04:14:44 +0000 Subject: [PATCH 6/9] Try to be clever, replicateA doesn't need by-name --- .../src/main/scala/cats/syntax/applicative.scala | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/core/src/main/scala/cats/syntax/applicative.scala b/core/src/main/scala/cats/syntax/applicative.scala index 78a202153e..db558bc458 100644 --- a/core/src/main/scala/cats/syntax/applicative.scala +++ b/core/src/main/scala/cats/syntax/applicative.scala @@ -25,26 +25,28 @@ package syntax trait ApplicativeSyntax { implicit final def catsSyntaxApplicativeId[A](a: A): ApplicativeIdOps[A] = new ApplicativeIdOps[A](a) + implicit final def catsSyntaxApplicative[F[_], A](fa: F[A]): ApplicativeOps[F, A] = + new ApplicativeOps[F, A](fa) implicit final def catsSyntaxApplicativeByName[F[_], A](fa: => F[A]): ApplicativeByNameOps[F, A] = new ApplicativeByNameOps[F, A](() => fa) - @deprecated("Use by-name version", "2.8.0") - final def catsSyntaxApplicative[F[_], A](fa: F[A]): ApplicativeOps[F, A] = - new ApplicativeOps[F, A](fa) } final class ApplicativeIdOps[A](private val a: A) extends AnyVal { def pure[F[_]](implicit F: Applicative[F]): F[A] = F.pure(a) } -@deprecated("Use by-name version", "2.8.0") final class ApplicativeOps[F[_], A](private val fa: F[A]) extends AnyVal { def replicateA(n: Int)(implicit F: Applicative[F]): F[List[A]] = F.replicateA(n, fa) - def unlessA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.unlessA(cond)(fa) - def whenA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.whenA(cond)(fa) +} + +object ApplicativeOps { + @deprecated("Use by-name version", "2.8.0") + def unlessA$extension[F[_], A](fa: F[A], cond: Boolean, F: Applicative[F]): F[Unit] = F.unlessA(cond)(fa) + @deprecated("Use by-name version", "2.8.0") + def whenA$extension[F[_], A](fa: F[A], cond: Boolean, F: Applicative[F]): F[Unit] = F.whenA(cond)(fa) } final class ApplicativeByNameOps[F[_], A](private val fa: () => F[A]) extends AnyVal { - def replicateA(n: Int)(implicit F: Applicative[F]): F[List[A]] = F.replicateA(n, fa()) def unlessA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.unlessA(cond)(fa()) def whenA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.whenA(cond)(fa()) } From 7e09cf68cf10929ced11ce19fcf84c3c9c7b10f6 Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Tue, 17 May 2022 04:16:40 +0000 Subject: [PATCH 7/9] Revert "Try to be clever, replicateA doesn't need by-name" This reverts commit 3e8eabad140c2b768f86fc411f9dc3b21dc5ab32. --- .../src/main/scala/cats/syntax/applicative.scala | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/core/src/main/scala/cats/syntax/applicative.scala b/core/src/main/scala/cats/syntax/applicative.scala index db558bc458..78a202153e 100644 --- a/core/src/main/scala/cats/syntax/applicative.scala +++ b/core/src/main/scala/cats/syntax/applicative.scala @@ -25,28 +25,26 @@ package syntax trait ApplicativeSyntax { implicit final def catsSyntaxApplicativeId[A](a: A): ApplicativeIdOps[A] = new ApplicativeIdOps[A](a) - implicit final def catsSyntaxApplicative[F[_], A](fa: F[A]): ApplicativeOps[F, A] = - new ApplicativeOps[F, A](fa) implicit final def catsSyntaxApplicativeByName[F[_], A](fa: => F[A]): ApplicativeByNameOps[F, A] = new ApplicativeByNameOps[F, A](() => fa) + @deprecated("Use by-name version", "2.8.0") + final def catsSyntaxApplicative[F[_], A](fa: F[A]): ApplicativeOps[F, A] = + new ApplicativeOps[F, A](fa) } final class ApplicativeIdOps[A](private val a: A) extends AnyVal { def pure[F[_]](implicit F: Applicative[F]): F[A] = F.pure(a) } +@deprecated("Use by-name version", "2.8.0") final class ApplicativeOps[F[_], A](private val fa: F[A]) extends AnyVal { def replicateA(n: Int)(implicit F: Applicative[F]): F[List[A]] = F.replicateA(n, fa) -} - -object ApplicativeOps { - @deprecated("Use by-name version", "2.8.0") - def unlessA$extension[F[_], A](fa: F[A], cond: Boolean, F: Applicative[F]): F[Unit] = F.unlessA(cond)(fa) - @deprecated("Use by-name version", "2.8.0") - def whenA$extension[F[_], A](fa: F[A], cond: Boolean, F: Applicative[F]): F[Unit] = F.whenA(cond)(fa) + def unlessA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.unlessA(cond)(fa) + def whenA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.whenA(cond)(fa) } final class ApplicativeByNameOps[F[_], A](private val fa: () => F[A]) extends AnyVal { + def replicateA(n: Int)(implicit F: Applicative[F]): F[List[A]] = F.replicateA(n, fa()) def unlessA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.unlessA(cond)(fa()) def whenA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.whenA(cond)(fa()) } From 78e815dada81ba9875fd4d2a655123ea0ac68076 Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Wed, 18 May 2022 14:39:03 +0000 Subject: [PATCH 8/9] Add `ApplicativeByValueOps`, mv `replicateA` there Co-authored-by: satorg --- core/src/main/scala/cats/syntax/applicative.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/syntax/applicative.scala b/core/src/main/scala/cats/syntax/applicative.scala index 78a202153e..19991e608e 100644 --- a/core/src/main/scala/cats/syntax/applicative.scala +++ b/core/src/main/scala/cats/syntax/applicative.scala @@ -27,6 +27,8 @@ trait ApplicativeSyntax { new ApplicativeIdOps[A](a) implicit final def catsSyntaxApplicativeByName[F[_], A](fa: => F[A]): ApplicativeByNameOps[F, A] = new ApplicativeByNameOps[F, A](() => fa) + implicit final def catsSyntaxApplicativeByValue[F[_], A](fa: F[A]): ApplicativeByValueOps[F, A] = + new ApplicativeByValueOps[F, A](fa) @deprecated("Use by-name version", "2.8.0") final def catsSyntaxApplicative[F[_], A](fa: F[A]): ApplicativeOps[F, A] = new ApplicativeOps[F, A](fa) @@ -43,8 +45,11 @@ final class ApplicativeOps[F[_], A](private val fa: F[A]) extends AnyVal { def whenA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.whenA(cond)(fa) } +final class ApplicativeByValueOps[F[_], A](private val fa: F[A]) extends AnyVal { + def replicateA(n: Int)(implicit F: Applicative[F]): F[List[A]] = F.replicateA(n, fa) +} + final class ApplicativeByNameOps[F[_], A](private val fa: () => F[A]) extends AnyVal { - def replicateA(n: Int)(implicit F: Applicative[F]): F[List[A]] = F.replicateA(n, fa()) def unlessA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.unlessA(cond)(fa()) def whenA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.whenA(cond)(fa()) } From 9dbef751eff9cefa3f7be2d990e50ab5e1f102d3 Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Wed, 18 May 2022 14:42:12 +0000 Subject: [PATCH 9/9] Clarify deprecation msg --- core/src/main/scala/cats/syntax/applicative.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/cats/syntax/applicative.scala b/core/src/main/scala/cats/syntax/applicative.scala index 19991e608e..e3b917b6aa 100644 --- a/core/src/main/scala/cats/syntax/applicative.scala +++ b/core/src/main/scala/cats/syntax/applicative.scala @@ -29,7 +29,7 @@ trait ApplicativeSyntax { new ApplicativeByNameOps[F, A](() => fa) implicit final def catsSyntaxApplicativeByValue[F[_], A](fa: F[A]): ApplicativeByValueOps[F, A] = new ApplicativeByValueOps[F, A](fa) - @deprecated("Use by-name version", "2.8.0") + @deprecated("Use by-value or by-name version", "2.8.0") final def catsSyntaxApplicative[F[_], A](fa: F[A]): ApplicativeOps[F, A] = new ApplicativeOps[F, A](fa) } @@ -38,7 +38,7 @@ final class ApplicativeIdOps[A](private val a: A) extends AnyVal { def pure[F[_]](implicit F: Applicative[F]): F[A] = F.pure(a) } -@deprecated("Use by-name version", "2.8.0") +@deprecated("Use by-value or by-name version", "2.8.0") final class ApplicativeOps[F[_], A](private val fa: F[A]) extends AnyVal { def replicateA(n: Int)(implicit F: Applicative[F]): F[List[A]] = F.replicateA(n, fa) def unlessA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.unlessA(cond)(fa)