Skip to content

Commit

Permalink
Merge pull request #4477 from mox692/fix/unnecessary_syntax_allocation
Browse files Browse the repository at this point in the history
Fix unnecessary syntax allocation
  • Loading branch information
armanbilge authored Aug 13, 2023
2 parents e835ecd + 288587e commit f496e25
Show file tree
Hide file tree
Showing 6 changed files with 268 additions and 9 deletions.
194 changes: 193 additions & 1 deletion core/src/main/scala/cats/syntax/apply.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,21 @@ package cats
package syntax

trait ApplySyntax extends TupleSemigroupalSyntax {
implicit final def catsSyntaxApply[F[_], A](fa: F[A])(implicit F: Apply[F]): Apply.Ops[F, A] =
@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] {
type TypeClassType = Apply[F]

val self = fa
val typeClassInstance = F
}

implicit final def catsSyntaxApplyFABOps[F[_], A, B](fab: F[A => B]): ApplyFABOps[F, A, B] =
new ApplyFABOps[F, A, B](fab)

implicit final def catsSyntaxApplyFABCOps[F[_], A, B, C](ff: F[(A, B) => C]): ApplyFABCOps[F, A, B, C] =
new ApplyFABCOps[F, A, B, C](ff)

implicit final def catsSyntaxApplyOps[F[_], A](fa: F[A]): ApplyOps[F, A] =
new ApplyOps(fa)
}
Expand All @@ -40,6 +47,73 @@ private[syntax] trait ApplySyntaxBinCompat0 {
new IfApplyOps[F](fa)
}

final class ApplyFABOps[F[_], A, B](private val fab: F[A => B]) extends AnyVal {

/**
* @see [[Apply.ap]].
*
* Example:
* {{{
* scala> import cats.syntax.all._
*
* scala> val someF: Option[Int => Long] = Some(_.toLong + 1L)
* scala> val noneF: Option[Int => Long] = None
* scala> val someInt: Option[Int] = Some(3)
* scala> val noneInt: Option[Int] = None
*
* scala> someF.ap(someInt)
* res0: Option[Long] = Some(4)
*
* scala> noneF.ap(someInt)
* res1: Option[Long] = None
*
* scala> someF.ap(noneInt)
* res2: Option[Long] = None
*
* scala> noneF.ap(noneInt)
* res3: Option[Long] = None
* }}}
*/
def ap(fa: F[A])(implicit F: Apply[F]): F[B] = F.ap(fab)(fa)

/**
* Alias for [[ap]].
*/
def <*>(fa: F[A])(implicit F: Apply[F]): F[B] = F.<*>[A, B](fab)(fa)
}

final class ApplyFABCOps[F[_], A, B, C](private val ff: F[(A, B) => C]) extends AnyVal {

/**
* @see [[Apply.ap2]].
*
* Example:
* {{{
* scala> import cats.syntax.all._
*
* scala> val someF: Option[(Int, Int) => Long] = Some((a, b) => (a + b).toLong)
* scala> val noneF: Option[(Int, Int) => Long] = None
* scala> val someInt1: Option[Int] = Some(3)
* scala> val someInt2: Option[Int] = Some(2)
* scala> val noneInt: Option[Int] = None
*
* scala> someF.ap2(someInt1, someInt2)
* res0: Option[Long] = Some(5)
*
* scala> noneF.ap2(someInt1, someInt2)
* res1: Option[Long] = None
*
* scala> someF.ap2(noneInt, noneInt)
* res2: Option[Long] = None
*
* 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)
}

final class IfApplyOps[F[_]](private val fcond: F[Boolean]) extends AnyVal {

@deprecated("Dangerous method, use ifM (a flatMap) or ifF (a map) instead", "2.6.2")
Expand All @@ -61,4 +135,122 @@ final class ApplyOps[F[_], A](private val fa: F[A]) extends AnyVal {
@deprecated("Use <* or productL instead.", "1.0.0-RC2")
@inline private[syntax] def forEffect[B](fb: F[B])(implicit F: Apply[F]): F[A] =
F.productL(fa)(fb)

/**
* @see [[Apply.productR]].
*
* Example:
* {{{
* scala> import cats.syntax.all._
* scala> import cats.data.Validated
* scala> import Validated.{Valid, Invalid}
*
* scala> type ErrOr[A] = Validated[String, A]
*
* scala> val validInt: ErrOr[Int] = Valid(3)
* scala> val validBool: ErrOr[Boolean] = Valid(true)
* scala> val invalidInt: ErrOr[Int] = Invalid("Invalid int.")
* scala> val invalidBool: ErrOr[Boolean] = Invalid("Invalid boolean.")
*
* scala> validInt.productR(validBool)
* res0: ErrOr[Boolean] = Valid(true)
*
* scala> invalidInt.productR(validBool)
* res1: ErrOr[Boolean] = Invalid(Invalid int.)
*
* scala> validInt.productR(invalidBool)
* res2: ErrOr[Boolean] = Invalid(Invalid boolean.)
*
* 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._
* scala> import cats.data.Validated
* scala> import Validated.{Valid, Invalid}
*
* scala> type ErrOr[A] = Validated[String, A]
*
* scala> val validInt: ErrOr[Int] = Valid(3)
* scala> val validBool: ErrOr[Boolean] = Valid(true)
* scala> val invalidInt: ErrOr[Int] = Invalid("Invalid int.")
* scala> val invalidBool: ErrOr[Boolean] = Invalid("Invalid boolean.")
*
* scala> validInt.productL(validBool)
* res0: ErrOr[Int] = Valid(3)
*
* scala> invalidInt.productL(validBool)
* res1: ErrOr[Int] = Invalid(Invalid int.)
*
* scala> validInt.productL(invalidBool)
* res2: ErrOr[Int] = Invalid(Invalid boolean.)
*
* 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)

/**
* Alias for [[productR]].
*/
def *>[B](fb: F[B])(implicit F: Apply[F]): F[B] = F.*>(fa)(fb)

/**
* Alias for [[productL]].
*/
def <*[B](fb: F[B])(implicit F: Apply[F]): F[A] = F.<*(fa)(fb)

/**
* @see [[Apply.map2]].
*
* Example:
* {{{
* scala> import cats.syntax.all._
*
* scala> val someInt: Option[Int] = Some(3)
* scala> val noneInt: Option[Int] = None
* scala> val someLong: Option[Long] = Some(4L)
* scala> val noneLong: Option[Long] = None
*
* scala> someInt.map2(someLong)((i, l) => i.toString + l.toString)
* res0: Option[String] = Some(34)
*
* scala> someInt.map2(noneLong)((i, l) => i.toString + l.toString)
* res0: Option[String] = None
*
* scala> noneInt.map2(noneLong)((i, l) => i.toString + l.toString)
* res0: Option[String] = None
*
* 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)
}
8 changes: 5 additions & 3 deletions core/src/main/scala/cats/syntax/contravariantMonoidal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ package syntax
import cats.ContravariantMonoidal

trait ContravariantMonoidalSyntax {
implicit final def catsSyntaxContravariantMonoidal[F[_], A](
fa: F[A]
)(implicit F: ContravariantMonoidal[F]): ContravariantMonoidalOps[F, A] =
@deprecated("Kept for binary compatibility", "2.10.0")
final def catsSyntaxContravariantMonoidal[F[_], A](
fa: F[A],
F: ContravariantMonoidal[F]
): ContravariantMonoidalOps[F, A] =
new ContravariantMonoidalOps[F, A] {
type TypeClassType = ContravariantMonoidal[F]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ package syntax
import cats.ContravariantSemigroupal

trait ContravariantSemigroupalSyntax extends TupleSemigroupalSyntax {
implicit final def catsSyntaxContravariantSemigroupal[F[_], A](
fa: F[A]
)(implicit F: ContravariantSemigroupal[F]): ContravariantSemigroupal.Ops[F, A] =
@deprecated("Kept for binary compatibility", "2.10.0")
final def catsSyntaxContravariantSemigroupal[F[_], A](
fa: F[A],
F: ContravariantSemigroupal[F]
): ContravariantSemigroupal.Ops[F, A] =
new ContravariantSemigroupal.Ops[F, A] {
type TypeClassType = ContravariantSemigroupal[F]

Expand Down
41 changes: 40 additions & 1 deletion core/src/main/scala/cats/syntax/semigroupal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,52 @@ package cats
package syntax

trait SemigroupalSyntax {
implicit final def catsSyntaxSemigroupal[F[_], A](fa: F[A])(implicit F: Semigroupal[F]): SemigroupalOps[F, A] =
@deprecated("Use `catsSyntaxSemigroupalOps2`", "2.10.0")
final def catsSyntaxSemigroupal[F[_], A](fa: F[A], F: Semigroupal[F]): SemigroupalOps[F, A] =
new SemigroupalOps[F, A] {
type TypeClassType = Semigroupal[F]

val self = fa
val typeClassInstance = F
}

implicit def catsSyntaxSemigroupalOps2[F[_], A](fa: F[A]): SemigroupalOps2[F, A] =
new SemigroupalOps2[F, A](fa)

}

final class SemigroupalOps2[F[_], A](private val fa: F[A]) extends AnyVal {

/**
* @see [[Semigroupal.product]]
*
* Example:
* {{{
* scala> import cats.syntax.all._
*
* scala> val noneInt: Option[Int] = None
* scala> val some3: Option[Int] = Some(3)
* scala> val noneString: Option[String] = None
* scala> val someFoo: Option[String] = Some("foo")
*
* scala> noneInt.product(noneString)
* res0: Option[(Int, String)] = None
*
* scala> noneInt.product(someFoo)
* res1: Option[(Int, String)] = None
*
* scala> some3.product(noneString)
* res2: Option[(Int, String)] = None
*
* scala> some3.product(someFoo)
* res3: Option[(Int, String)] = Some((3,foo))
* }}}
*/
def product[B](fb: F[B])(implicit F: Semigroupal[F]): F[(A, B)] = F.product(fa, fb)

@deprecated("Replaced by an apply syntax, e.g. instead of (a |@| b).map(...) use (a, b).mapN(...)", "1.0.0-MF")
def |@|[B](fb: F[B]): SemigroupalBuilder[F]#SemigroupalBuilder2[A, B] =
new SemigroupalBuilder[F] |@| fa |@| fb
}

abstract class SemigroupalOps[F[_], A] extends Semigroupal.Ops[F, A] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ package cats.tests

import cats.laws.discipline.SerializableTests
import cats.syntax.either._
import cats.Semigroupal

/**
* Test that our syntax implicits are serializable.
Expand All @@ -36,7 +37,7 @@ class SyntaxSerializationSuite extends CatsSuite {
)

checkAll("SemigroupalOps[Option, Int]",
SerializableTests.serializable(cats.syntax.all.catsSyntaxSemigroupal[Option, Int](None))
SerializableTests.serializable(cats.syntax.all.catsSyntaxSemigroupal[Option, Int](None, Semigroupal[Option]))
)

checkAll(
Expand Down
23 changes: 23 additions & 0 deletions tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,8 @@ object SyntaxSuite {
val f = mock[(A, B, C) => Z]
val ff = mock[F[(A, B, C) => Z]]

fa.productR(fb)
fa.productL(fb)
fa *> fb
fb <* fc

Expand All @@ -394,6 +396,19 @@ object SyntaxSuite {

thabcde.imapN(f5)(g5)
(ha, hb, hc, hd, he).imapN(f5)(g5)

val tfab = mock[F[A => B]]
tfab.ap(fa)
tfab <*> fa

val tabcf = mock[F[(A, B) => C]]
tabcf.ap2(fa, fb)

val tabc = mock[(A, B) => C]
fa.map2(fb)(tabc)

val tEvalfb = mock[Eval[F[B]]]
fa.map2Eval(tEvalfb)(tabc)
}

def testBifoldable[F[_, _]: Bifoldable, A, B, C, D: Monoid]: Unit = {
Expand Down Expand Up @@ -688,4 +703,12 @@ object SyntaxSuite {

val result: Option[List[B]] = list.traverseCollect(f)
}

def testSemigroupal[F[_]: Semigroupal, A, B]: Unit = {
val fa = mock[F[A]]
val fb = mock[F[B]]

fa.product(fb)
fa |@| fb
}
}

0 comments on commit f496e25

Please sign in to comment.