Skip to content

Commit

Permalink
Merge pull request #4340 from kubukoz/add-function-applyn
Browse files Browse the repository at this point in the history
Add FunctionN.liftN, parLiftN
  • Loading branch information
satorg authored Sep 29, 2024
2 parents f4aec7f + 8d53b58 commit 29dd05a
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 17 deletions.
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 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 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)
-}
"""
}
}
}
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

0 comments on commit 29dd05a

Please sign in to comment.