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

Fix unnecessary syntax allocation #4477

Merged
merged 28 commits into from
Aug 13, 2023
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
99e9a71
fix apply syntax allocation
mox692 Jul 11, 2023
af45152
not remove original ops
mox692 Jul 11, 2023
ef328b8
fix ApplyBinCompat1 naming
mox692 Jul 11, 2023
0ba479b
remove implicit for F
mox692 Jul 19, 2023
0c642f6
Add deprecate annotation to old ops
mox692 Jul 20, 2023
e7d544f
create another syntax classes
mox692 Jul 23, 2023
7ca6743
Add doc comments
mox692 Jul 23, 2023
2f5dc8f
fix naming
mox692 Jul 23, 2023
0ee124e
Fix: implicit method type parameter
mox692 Jul 24, 2023
f97affd
Fix: group together F[A] syntax
mox692 Jul 24, 2023
e1ca164
Add: new Semigroupal syntax with no unnecessary allocations
mox692 Jul 24, 2023
5603fee
Fix: failing doctest
mox692 Jul 24, 2023
fc6ac27
Fix: failing doc test
mox692 Jul 25, 2023
342996f
remove implicit from catsSyntaxSemigroupal
mox692 Jul 26, 2023
649c38c
Workaround to the failing test
mox692 Jul 26, 2023
9ebe3a7
Deprecate some synax causing allocation
mox692 Jul 26, 2023
1962dc3
Fix: typo
mox692 Jul 28, 2023
1d5102f
Fix: delete ApplySyntaxBinCompat1
mox692 Jul 28, 2023
c18c981
Move ApplyOps2
mox692 Jul 28, 2023
e74b301
Rename Apply2Ops to ApplyFABCOps
mox692 Jul 28, 2023
0fbc093
Uncurry implicits
mox692 Jul 28, 2023
3f1e8e5
Fix: typo
mox692 Jul 28, 2023
f09b400
Tidying
mox692 Jul 28, 2023
8ae9b9c
Remove unrelated diff
mox692 Jul 28, 2023
a0fdb0b
Merge `ApplyOps` and `ApplyOps2`
armanbilge Aug 2, 2023
119f350
Remove spurious whitespace change
armanbilge Aug 3, 2023
5c06b8a
Remove `Serializable` from `ApplyFABOps`
armanbilge Aug 3, 2023
288587e
Add tests related to the change
mox692 Aug 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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] =
mox692 marked this conversation as resolved.
Show resolved Hide resolved
@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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just came by to say this is a cool naming 😄


/**
* @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
Comment on lines +69 to +71
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This syntax is already deprecated, but I added it to the new syntax class for compatibility.

}

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