Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FunctionN.liftN, parLiftN #4340

Merged
merged 11 commits into from
Sep 29, 2024
28 changes: 14 additions & 14 deletions core/src/main/scala/cats/syntax/apply.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
package cats
package syntax

trait ApplySyntax extends TupleSemigroupalSyntax {
trait ApplySyntax extends TupleSemigroupalSyntax with FunctionApplySyntax {
@deprecated("Kept for binary compatibility", "2.10.0")
final def catsSyntaxApply[F[_], A](fa: F[A], F: Apply[F]): Apply.Ops[F, A] =
new Apply.Ops[F, A] {
Expand Down Expand Up @@ -51,7 +51,7 @@ final class ApplyFABOps[F[_], A, B](private val fab: F[A => B]) extends AnyVal {

/**
* @see [[Apply.ap]].
*
*
* Example:
* {{{
* scala> import cats.syntax.all._
Expand Down Expand Up @@ -86,7 +86,7 @@ final class ApplyFABCOps[F[_], A, B, C](private val ff: F[(A, B) => C]) extends

/**
* @see [[Apply.ap2]].
*
*
* Example:
* {{{
* scala> import cats.syntax.all._
Expand All @@ -109,7 +109,7 @@ final class ApplyFABCOps[F[_], A, B, C](private val ff: F[(A, B) => C]) extends
* scala> noneF.ap2(noneInt, noneInt)
* res3: Option[Long] = None
* }}}
*
*
*/
def ap2(fa: F[A], fb: F[B])(implicit F: Apply[F]): F[C] = F.ap2(ff)(fa, fb)
}
Expand Down Expand Up @@ -138,7 +138,7 @@ final class ApplyOps[F[_], A](private val fa: F[A]) extends AnyVal {

/**
* @see [[Apply.productR]].
*
*
* Example:
* {{{
* scala> import cats.syntax.all._
Expand All @@ -163,13 +163,13 @@ final class ApplyOps[F[_], A](private val fa: F[A]) extends AnyVal {
*
* scala> invalidInt.productR(invalidBool)
* res3: ErrOr[Boolean] = Invalid(Invalid int.Invalid boolean.)
* }}}
* }}}
*/
def productR[B](fb: F[B])(implicit F: Apply[F]): F[B] = F.productR(fa)(fb)

/**
* @see [[Apply.productL]].
*
*
* Example:
* {{{
* scala> import cats.syntax.all._
Expand All @@ -194,7 +194,7 @@ final class ApplyOps[F[_], A](private val fa: F[A]) extends AnyVal {
*
* scala> invalidInt.productL(invalidBool)
* res3: ErrOr[Int] = Invalid(Invalid int.Invalid boolean.)
* }}}
* }}}
*/
def productL[B](fb: F[B])(implicit F: Apply[F]): F[A] = F.productL(fa)(fb)

Expand All @@ -210,7 +210,7 @@ final class ApplyOps[F[_], A](private val fa: F[A]) extends AnyVal {

/**
* @see [[Apply.map2]].
*
*
* Example:
* {{{
* scala> import cats.syntax.all._
Expand All @@ -231,25 +231,25 @@ final class ApplyOps[F[_], A](private val fa: F[A]) extends AnyVal {
*
* scala> noneInt.map2(someLong)((i, l) => i.toString + l.toString)
* res0: Option[String] = None
* }}}
* }}}
*/
def map2[B, C](fb: F[B])(f: (A, B) => C)(implicit F: Apply[F]): F[C] =
F.map2(fa, fb)(f)

/**
* @see [[Apply.map2Eval]].
*
*
* Example:
* {{{
* scala> import cats.{Eval, Later}
* scala> import cats.syntax.all._
*
*
* scala> val bomb: Eval[Option[Int]] = Later(sys.error("boom"))
* scala> val x: Option[Int] = None
*
*
* scala> x.map2Eval(bomb)(_ + _).value
* res0: Option[Int] = None
* }}}
* }}}
*/
def map2Eval[B, C](fb: Eval[F[B]])(f: (A, B) => C)(implicit F: Apply[F]): Eval[F[C]] =
F.map2Eval(fa, fb)(f)
Expand Down
44 changes: 41 additions & 3 deletions project/Boilerplate.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ object Boilerplate {
GenParallelArityFunctions,
GenParallelArityFunctions2,
GenFoldableArityFunctions,
GenFunctionSyntax,
GenTupleParallelSyntax,
GenTupleShowInstances,
GenTupleMonadInstances,
Expand Down Expand Up @@ -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:
| * {{{
Expand All @@ -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
| *
| */
Expand All @@ -621,4 +622,41 @@ 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 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 {
- 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)
-}
"""
}
}
}
48 changes: 48 additions & 0 deletions tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,54 @@ object SyntaxSuite {
tfa.parFlatMap(mfone)
}

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.liftN(fa, fb)

result2: F[T]

val fapply3 = mock[(A, B, C) => T]

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 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]]
Expand Down