From 483a31430c080b0821769a0a6d8124b1faec3e21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Koz=C5=82owski?= Date: Sat, 12 Nov 2022 23:33:52 +0100 Subject: [PATCH 1/8] Add FunctionN.applyN --- core/src/main/scala/cats/syntax/apply.scala | 2 +- project/Boilerplate.scala | 42 +++++++++++++++++-- .../test/scala/cats/tests/SyntaxSuite.scala | 18 ++++++++ 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/core/src/main/scala/cats/syntax/apply.scala b/core/src/main/scala/cats/syntax/apply.scala index 868647b5dc..293f132479 100644 --- a/core/src/main/scala/cats/syntax/apply.scala +++ b/core/src/main/scala/cats/syntax/apply.scala @@ -22,7 +22,7 @@ package cats package syntax -trait ApplySyntax extends TupleSemigroupalSyntax { +trait ApplySyntax extends TupleSemigroupalSyntax with FunctionApplySyntax { implicit final def catsSyntaxApply[F[_], A](fa: F[A])(implicit F: Apply[F]): Apply.Ops[F, A] = new Apply.Ops[F, A] { type TypeClassType = Apply[F] diff --git a/project/Boilerplate.scala b/project/Boilerplate.scala index 996e385dab..fa6845c2da 100644 --- a/project/Boilerplate.scala +++ b/project/Boilerplate.scala @@ -32,6 +32,7 @@ object Boilerplate { GenParallelArityFunctions, GenParallelArityFunctions2, GenFoldableArityFunctions, + GenFunctionSyntax, GenTupleParallelSyntax, GenTupleShowInstances, GenTupleMonadInstances, @@ -594,7 +595,7 @@ object Boilerplate { | * @groupname FoldableSlidingN foldable arity | * @groupdesc FoldableSlidingN | * Group sequential elements into fixed sized tuples by passing a "sliding window" over them. - | * + | * | * A foldable with fewer elements than the window size will return an empty list unlike `Iterable#sliding(size: Int)`. | * Example: | * {{{ @@ -604,12 +605,12 @@ object Boilerplate { | * | * scala> Foldable[List].sliding4((1 to 10).toList) | * val res1: List[(Int, Int, Int, Int)] = List((1,2,3,4), (2,3,4,5), (3,4,5,6), (4,5,6,7), (5,6,7,8), (6,7,8,9), (7,8,9,10)) - | * + | * | * scala> Foldable[List].sliding4((1 to 2).toList) | * val res2: List[(Int, Int, Int, Int)] = List() | * | * }}} - | * + | * | * @groupprio FoldableSlidingN 999 | * | */ @@ -621,4 +622,39 @@ object Boilerplate { """ } } + + object GenFunctionSyntax extends Template { + def filename(root: File) = root / "cats" / "syntax" / "FunctionApplySyntax.scala" + + override def range = 2 to maxArity + + def content(tv: TemplateVals) = { + import tv._ + + val function = s"Function$arity[${`A..N`}, T]" + + val typedParams = synVals.zip(synTypes).map { case (v, t) => s"$v: F[$t]" }.mkString(", ") + + block""" + |package cats + |package syntax + | + |import cats.Functor + |import cats.Semigroupal + | + |trait FunctionApplySyntax { + | implicit def catsSyntaxFunction1Apply[T, A0](f: Function1[A0, T]): Function1ApplyOps[T, A0] = new Function1ApplyOps(f) + - implicit def catsSyntaxFunction${arity}Apply[T, ${`A..N`}](f: $function): Function${arity}ApplyOps[T, ${`A..N`}] = new Function${arity}ApplyOps(f) + |} + | + |private[syntax] final class Function1ApplyOps[T, A0](private val f: Function1[A0, T]) extends Serializable { + | def applyN[F[_]: Functor](a0: F[A0]): F[T] = Functor[F].map(a0)(f) + |} + | + -private[syntax] final class Function${arity}ApplyOps[T, ${`A..N`}](private val f: $function) extends Serializable { + - def applyN[F[_]: Functor: Semigroupal]($typedParams): F[T] = Semigroupal.map$arity(${`a..n`})(f) + -} + """ + } + } } diff --git a/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala b/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala index 79abb123d9..b62fbc4e24 100644 --- a/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala +++ b/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala @@ -269,6 +269,24 @@ object SyntaxSuite { tfa.parFlatMap(mfone) } + def testApplyN[F[_]: Apply, A, B, C, T] = { + val fa = mock[F[A]] + val fb = mock[F[B]] + val fc = mock[F[C]] + + val fapply2 = mock[(A, B) => T] + + val result2 = fapply2.applyN(fa, fb) + + result2: F[T] + + val fapply3 = mock[(A, B, C) => T] + + val result3 = fapply3.applyN(fa, fb, fc) + + result3: F[T] + } + def testParallelBi[M[_], F[_], T[_, _]: Bitraverse, A, B, C, D](implicit P: Parallel.Aux[M, F]): Unit = { val tab = mock[T[A, B]] val f = mock[A => M[C]] From ca01c0e7dc37285c0bcd0b6b6f28bcb9254e9aa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Koz=C5=82owski?= Date: Sat, 12 Nov 2022 23:49:09 +0100 Subject: [PATCH 2/8] Add tupledF for good measure --- project/Boilerplate.scala | 2 ++ tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/project/Boilerplate.scala b/project/Boilerplate.scala index fa6845c2da..e85c63f519 100644 --- a/project/Boilerplate.scala +++ b/project/Boilerplate.scala @@ -649,10 +649,12 @@ object Boilerplate { | |private[syntax] final class Function1ApplyOps[T, A0](private val f: Function1[A0, T]) extends Serializable { | def applyN[F[_]: Functor](a0: F[A0]): F[T] = Functor[F].map(a0)(f) + | def tupledF[F[_]: Functor](t: F[A0]): F[T] = Functor[F].map(t)(f) |} | -private[syntax] final class Function${arity}ApplyOps[T, ${`A..N`}](private val f: $function) extends Serializable { - def applyN[F[_]: Functor: Semigroupal]($typedParams): F[T] = Semigroupal.map$arity(${`a..n`})(f) + - def tupledF[F[_]: Functor](t: F[${`(A..N)`}]): F[T] = Functor[F].map(t)(f.tupled) -} """ } diff --git a/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala b/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala index b62fbc4e24..2cdd239c93 100644 --- a/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala +++ b/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala @@ -285,6 +285,10 @@ object SyntaxSuite { val result3 = fapply3.applyN(fa, fb, fc) result3: F[T] + + val result3Tupled = fapply3.tupledF((fa, fb, fc).tupled) + + result3Tupled: F[T] } def testParallelBi[M[_], F[_], T[_, _]: Bitraverse, A, B, C, D](implicit P: Parallel.Aux[M, F]): Unit = { From eedb579116a60cf27ca0f0dc754658611d2af55c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Koz=C5=82owski?= Date: Sat, 1 Jul 2023 03:41:04 +0200 Subject: [PATCH 3/8] Rename applyN to liftN, add compilation test for case 1 --- project/Boilerplate.scala | 4 ++-- .../src/test/scala/cats/tests/SyntaxSuite.scala | 12 +++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/project/Boilerplate.scala b/project/Boilerplate.scala index e85c63f519..e160c1b2ac 100644 --- a/project/Boilerplate.scala +++ b/project/Boilerplate.scala @@ -648,12 +648,12 @@ object Boilerplate { |} | |private[syntax] final class Function1ApplyOps[T, A0](private val f: Function1[A0, T]) extends Serializable { - | def applyN[F[_]: Functor](a0: F[A0]): F[T] = Functor[F].map(a0)(f) + | def liftN[F[_]: Functor](a0: F[A0]): F[T] = Functor[F].map(a0)(f) | def tupledF[F[_]: Functor](t: F[A0]): F[T] = Functor[F].map(t)(f) |} | -private[syntax] final class Function${arity}ApplyOps[T, ${`A..N`}](private val f: $function) extends Serializable { - - def applyN[F[_]: Functor: Semigroupal]($typedParams): F[T] = Semigroupal.map$arity(${`a..n`})(f) + - def liftN[F[_]: Functor: Semigroupal]($typedParams): F[T] = Semigroupal.map$arity(${`a..n`})(f) - def tupledF[F[_]: Functor](t: F[${`(A..N)`}]): F[T] = Functor[F].map(t)(f.tupled) -} """ diff --git a/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala b/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala index 9ab10f47f2..ec937585d7 100644 --- a/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala +++ b/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala @@ -286,20 +286,26 @@ object SyntaxSuite { tfa.parFlatMap(mfone) } - def testApplyN[F[_]: Apply, A, B, C, T] = { + def testliftN[F[_]: Apply, A, B, C, T] = { val fa = mock[F[A]] val fb = mock[F[B]] val fc = mock[F[C]] + val fapply1 = mock[A => T] + + val result1 = fapply1.liftN(fa) + + result1: F[T] + val fapply2 = mock[(A, B) => T] - val result2 = fapply2.applyN(fa, fb) + val result2 = fapply2.liftN(fa, fb) result2: F[T] val fapply3 = mock[(A, B, C) => T] - val result3 = fapply3.applyN(fa, fb, fc) + val result3 = fapply3.liftN(fa, fb, fc) result3: F[T] From a92e469c181d63682a64fbd3bd1f087d473d312c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Koz=C5=82owski?= Date: Sat, 1 Jul 2023 03:43:24 +0200 Subject: [PATCH 4/8] Add tupledF arity-1 test --- tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala b/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala index ec937585d7..9ad8bc7a69 100644 --- a/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala +++ b/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala @@ -309,6 +309,10 @@ object SyntaxSuite { result3: F[T] + val result1Tupled = fapply1.tupledF(fa) + + result1Tupled: F[T] + val result3Tupled = fapply3.tupledF((fa, fb, fc).tupled) result3Tupled: F[T] From 5857d385591aa1c1b9a7018dea5d49dbf525c26a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Koz=C5=82owski?= Date: Thu, 4 Jan 2024 02:19:19 +0100 Subject: [PATCH 5/8] Add parLiftN --- project/Boilerplate.scala | 2 + .../test/scala/cats/tests/SyntaxSuite.scala | 42 ++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/project/Boilerplate.scala b/project/Boilerplate.scala index e160c1b2ac..cc777af3cd 100644 --- a/project/Boilerplate.scala +++ b/project/Boilerplate.scala @@ -649,11 +649,13 @@ object Boilerplate { | |private[syntax] final class Function1ApplyOps[T, A0](private val f: Function1[A0, T]) extends Serializable { | def liftN[F[_]: Functor](a0: F[A0]): F[T] = Functor[F].map(a0)(f) + | def parLiftN[F[_]: Functor](a0: F[A0]): F[T] = Functor[F].map(a0)(f) | def tupledF[F[_]: Functor](t: F[A0]): F[T] = Functor[F].map(t)(f) |} | -private[syntax] final class Function${arity}ApplyOps[T, ${`A..N`}](private val f: $function) extends Serializable { - def liftN[F[_]: Functor: Semigroupal]($typedParams): F[T] = Semigroupal.map$arity(${`a..n`})(f) + - def parLiftN[F[_]: Parallel]($typedParams): F[T] = Parallel.parMap$arity(${`a..n`})(f) - def tupledF[F[_]: Functor](t: F[${`(A..N)`}]): F[T] = Functor[F].map(t)(f.tupled) -} """ diff --git a/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala b/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala index a2150f1b98..5a4d405015 100644 --- a/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala +++ b/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala @@ -286,7 +286,7 @@ object SyntaxSuite { tfa.parFlatMap(mfone) } - def testliftN[F[_]: Apply, A, B, C, T] = { + def testLiftN[F[_]: Apply, A, B, C, T] = { val fa = mock[F[A]] val fb = mock[F[B]] val fc = mock[F[C]] @@ -308,12 +308,50 @@ object SyntaxSuite { val result3 = fapply3.liftN(fa, fb, fc) result3: F[T] + } + + def testParLiftN[F[_]: Parallel: Functor, A, B, C, T] = { + val fa = mock[F[A]] + val fb = mock[F[B]] + val fc = mock[F[C]] + + val fapply1 = mock[A => T] + + val result1 = fapply1.parLiftN(fa) + + result1: F[T] + + val fapply2 = mock[(A, B) => T] + + val result2 = fapply2.parLiftN(fa, fb) + + result2: F[T] + + val fapply3 = mock[(A, B, C) => T] + + val result3 = fapply3.parLiftN(fa, fb, fc) + + result3: F[T] + } + + def testTupledF[F[_]: Apply, A, B, C, T] = { + val fa = mock[F[A]] + val fab = mock[F[(A, B)]] + val fabc = mock[F[(A, B, C)]] + + val fapply1 = mock[A => T] + val fapply2 = mock[(A, B) => T] + val fapply3 = mock[(A, B, C) => T] val result1Tupled = fapply1.tupledF(fa) result1Tupled: F[T] - val result3Tupled = fapply3.tupledF((fa, fb, fc).tupled) + val result2Tupled = fapply2.tupledF(fab) + + result2Tupled: F[T] + + val result3Tupled = fapply3.tupledF(fabc) result3Tupled: F[T] } From 2010a2576fb2e63891cfde28ca9d05a9fa622744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Koz=C5=82owski?= Date: Tue, 27 Feb 2024 12:25:48 +0100 Subject: [PATCH 6/8] Remove tupledF --- project/Boilerplate.scala | 2 -- .../test/scala/cats/tests/SyntaxSuite.scala | 22 ------------------- 2 files changed, 24 deletions(-) diff --git a/project/Boilerplate.scala b/project/Boilerplate.scala index cc777af3cd..2f95f75a16 100644 --- a/project/Boilerplate.scala +++ b/project/Boilerplate.scala @@ -650,13 +650,11 @@ object Boilerplate { |private[syntax] final class Function1ApplyOps[T, A0](private val f: Function1[A0, T]) extends Serializable { | def liftN[F[_]: Functor](a0: F[A0]): F[T] = Functor[F].map(a0)(f) | def parLiftN[F[_]: Functor](a0: F[A0]): F[T] = Functor[F].map(a0)(f) - | def tupledF[F[_]: Functor](t: F[A0]): F[T] = Functor[F].map(t)(f) |} | -private[syntax] final class Function${arity}ApplyOps[T, ${`A..N`}](private val f: $function) extends Serializable { - def liftN[F[_]: Functor: Semigroupal]($typedParams): F[T] = Semigroupal.map$arity(${`a..n`})(f) - def parLiftN[F[_]: Parallel]($typedParams): F[T] = Parallel.parMap$arity(${`a..n`})(f) - - def tupledF[F[_]: Functor](t: F[${`(A..N)`}]): F[T] = Functor[F].map(t)(f.tupled) -} """ } diff --git a/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala b/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala index 5a4d405015..2f8e82e4bd 100644 --- a/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala +++ b/tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala @@ -334,28 +334,6 @@ object SyntaxSuite { result3: F[T] } - def testTupledF[F[_]: Apply, A, B, C, T] = { - val fa = mock[F[A]] - val fab = mock[F[(A, B)]] - val fabc = mock[F[(A, B, C)]] - - val fapply1 = mock[A => T] - val fapply2 = mock[(A, B) => T] - val fapply3 = mock[(A, B, C) => T] - - val result1Tupled = fapply1.tupledF(fa) - - result1Tupled: F[T] - - val result2Tupled = fapply2.tupledF(fab) - - result2Tupled: F[T] - - val result3Tupled = fapply3.tupledF(fabc) - - result3Tupled: F[T] - } - def testParallelBi[M[_], F[_], T[_, _]: Bitraverse, A, B, C, D](implicit P: Parallel.Aux[M, F]): Unit = { val tab = mock[T[A, B]] val f = mock[A => M[C]] From 5c47f341f633a417ba1059092c8c5514ac283917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Koz=C5=82owski?= Date: Tue, 27 Feb 2024 19:18:10 +0100 Subject: [PATCH 7/8] empty commit From 8d53b5806f10d85d7cd6aea1113787e4e7887c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Koz=C5=82owski?= Date: Sun, 29 Sep 2024 20:03:22 +0200 Subject: [PATCH 8/8] Add AnyVal --- project/Boilerplate.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Boilerplate.scala b/project/Boilerplate.scala index 2f95f75a16..277a967cd6 100644 --- a/project/Boilerplate.scala +++ b/project/Boilerplate.scala @@ -647,12 +647,12 @@ object Boilerplate { - implicit def catsSyntaxFunction${arity}Apply[T, ${`A..N`}](f: $function): Function${arity}ApplyOps[T, ${`A..N`}] = new Function${arity}ApplyOps(f) |} | - |private[syntax] final class Function1ApplyOps[T, A0](private val f: Function1[A0, T]) extends Serializable { + |private[syntax] final class Function1ApplyOps[T, A0](private val f: Function1[A0, T]) extends AnyVal with Serializable { | def liftN[F[_]: Functor](a0: F[A0]): F[T] = Functor[F].map(a0)(f) | def parLiftN[F[_]: Functor](a0: F[A0]): F[T] = Functor[F].map(a0)(f) |} | - -private[syntax] final class Function${arity}ApplyOps[T, ${`A..N`}](private val f: $function) extends Serializable { + -private[syntax] final class Function${arity}ApplyOps[T, ${`A..N`}](private val f: $function) extends AnyVal with Serializable { - def liftN[F[_]: Functor: Semigroupal]($typedParams): F[T] = Semigroupal.map$arity(${`a..n`})(f) - def parLiftN[F[_]: Parallel]($typedParams): F[T] = Parallel.parMap$arity(${`a..n`})(f) -}