Skip to content

Commit f496e25

Browse files
authored
Merge pull request #4477 from mox692/fix/unnecessary_syntax_allocation
Fix unnecessary syntax allocation
2 parents e835ecd + 288587e commit f496e25

File tree

6 files changed

+268
-9
lines changed

6 files changed

+268
-9
lines changed

core/src/main/scala/cats/syntax/apply.scala

+193-1
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,21 @@ package cats
2323
package syntax
2424

2525
trait ApplySyntax extends TupleSemigroupalSyntax {
26-
implicit final def catsSyntaxApply[F[_], A](fa: F[A])(implicit F: Apply[F]): Apply.Ops[F, A] =
26+
@deprecated("Kept for binary compatibility", "2.10.0")
27+
final def catsSyntaxApply[F[_], A](fa: F[A], F: Apply[F]): Apply.Ops[F, A] =
2728
new Apply.Ops[F, A] {
2829
type TypeClassType = Apply[F]
2930

3031
val self = fa
3132
val typeClassInstance = F
3233
}
3334

35+
implicit final def catsSyntaxApplyFABOps[F[_], A, B](fab: F[A => B]): ApplyFABOps[F, A, B] =
36+
new ApplyFABOps[F, A, B](fab)
37+
38+
implicit final def catsSyntaxApplyFABCOps[F[_], A, B, C](ff: F[(A, B) => C]): ApplyFABCOps[F, A, B, C] =
39+
new ApplyFABCOps[F, A, B, C](ff)
40+
3441
implicit final def catsSyntaxApplyOps[F[_], A](fa: F[A]): ApplyOps[F, A] =
3542
new ApplyOps(fa)
3643
}
@@ -40,6 +47,73 @@ private[syntax] trait ApplySyntaxBinCompat0 {
4047
new IfApplyOps[F](fa)
4148
}
4249

50+
final class ApplyFABOps[F[_], A, B](private val fab: F[A => B]) extends AnyVal {
51+
52+
/**
53+
* @see [[Apply.ap]].
54+
*
55+
* Example:
56+
* {{{
57+
* scala> import cats.syntax.all._
58+
*
59+
* scala> val someF: Option[Int => Long] = Some(_.toLong + 1L)
60+
* scala> val noneF: Option[Int => Long] = None
61+
* scala> val someInt: Option[Int] = Some(3)
62+
* scala> val noneInt: Option[Int] = None
63+
*
64+
* scala> someF.ap(someInt)
65+
* res0: Option[Long] = Some(4)
66+
*
67+
* scala> noneF.ap(someInt)
68+
* res1: Option[Long] = None
69+
*
70+
* scala> someF.ap(noneInt)
71+
* res2: Option[Long] = None
72+
*
73+
* scala> noneF.ap(noneInt)
74+
* res3: Option[Long] = None
75+
* }}}
76+
*/
77+
def ap(fa: F[A])(implicit F: Apply[F]): F[B] = F.ap(fab)(fa)
78+
79+
/**
80+
* Alias for [[ap]].
81+
*/
82+
def <*>(fa: F[A])(implicit F: Apply[F]): F[B] = F.<*>[A, B](fab)(fa)
83+
}
84+
85+
final class ApplyFABCOps[F[_], A, B, C](private val ff: F[(A, B) => C]) extends AnyVal {
86+
87+
/**
88+
* @see [[Apply.ap2]].
89+
*
90+
* Example:
91+
* {{{
92+
* scala> import cats.syntax.all._
93+
*
94+
* scala> val someF: Option[(Int, Int) => Long] = Some((a, b) => (a + b).toLong)
95+
* scala> val noneF: Option[(Int, Int) => Long] = None
96+
* scala> val someInt1: Option[Int] = Some(3)
97+
* scala> val someInt2: Option[Int] = Some(2)
98+
* scala> val noneInt: Option[Int] = None
99+
*
100+
* scala> someF.ap2(someInt1, someInt2)
101+
* res0: Option[Long] = Some(5)
102+
*
103+
* scala> noneF.ap2(someInt1, someInt2)
104+
* res1: Option[Long] = None
105+
*
106+
* scala> someF.ap2(noneInt, noneInt)
107+
* res2: Option[Long] = None
108+
*
109+
* scala> noneF.ap2(noneInt, noneInt)
110+
* res3: Option[Long] = None
111+
* }}}
112+
*
113+
*/
114+
def ap2(fa: F[A], fb: F[B])(implicit F: Apply[F]): F[C] = F.ap2(ff)(fa, fb)
115+
}
116+
43117
final class IfApplyOps[F[_]](private val fcond: F[Boolean]) extends AnyVal {
44118

45119
@deprecated("Dangerous method, use ifM (a flatMap) or ifF (a map) instead", "2.6.2")
@@ -61,4 +135,122 @@ final class ApplyOps[F[_], A](private val fa: F[A]) extends AnyVal {
61135
@deprecated("Use <* or productL instead.", "1.0.0-RC2")
62136
@inline private[syntax] def forEffect[B](fb: F[B])(implicit F: Apply[F]): F[A] =
63137
F.productL(fa)(fb)
138+
139+
/**
140+
* @see [[Apply.productR]].
141+
*
142+
* Example:
143+
* {{{
144+
* scala> import cats.syntax.all._
145+
* scala> import cats.data.Validated
146+
* scala> import Validated.{Valid, Invalid}
147+
*
148+
* scala> type ErrOr[A] = Validated[String, A]
149+
*
150+
* scala> val validInt: ErrOr[Int] = Valid(3)
151+
* scala> val validBool: ErrOr[Boolean] = Valid(true)
152+
* scala> val invalidInt: ErrOr[Int] = Invalid("Invalid int.")
153+
* scala> val invalidBool: ErrOr[Boolean] = Invalid("Invalid boolean.")
154+
*
155+
* scala> validInt.productR(validBool)
156+
* res0: ErrOr[Boolean] = Valid(true)
157+
*
158+
* scala> invalidInt.productR(validBool)
159+
* res1: ErrOr[Boolean] = Invalid(Invalid int.)
160+
*
161+
* scala> validInt.productR(invalidBool)
162+
* res2: ErrOr[Boolean] = Invalid(Invalid boolean.)
163+
*
164+
* scala> invalidInt.productR(invalidBool)
165+
* res3: ErrOr[Boolean] = Invalid(Invalid int.Invalid boolean.)
166+
* }}}
167+
*/
168+
def productR[B](fb: F[B])(implicit F: Apply[F]): F[B] = F.productR(fa)(fb)
169+
170+
/**
171+
* @see [[Apply.productL]].
172+
*
173+
* Example:
174+
* {{{
175+
* scala> import cats.syntax.all._
176+
* scala> import cats.data.Validated
177+
* scala> import Validated.{Valid, Invalid}
178+
*
179+
* scala> type ErrOr[A] = Validated[String, A]
180+
*
181+
* scala> val validInt: ErrOr[Int] = Valid(3)
182+
* scala> val validBool: ErrOr[Boolean] = Valid(true)
183+
* scala> val invalidInt: ErrOr[Int] = Invalid("Invalid int.")
184+
* scala> val invalidBool: ErrOr[Boolean] = Invalid("Invalid boolean.")
185+
*
186+
* scala> validInt.productL(validBool)
187+
* res0: ErrOr[Int] = Valid(3)
188+
*
189+
* scala> invalidInt.productL(validBool)
190+
* res1: ErrOr[Int] = Invalid(Invalid int.)
191+
*
192+
* scala> validInt.productL(invalidBool)
193+
* res2: ErrOr[Int] = Invalid(Invalid boolean.)
194+
*
195+
* scala> invalidInt.productL(invalidBool)
196+
* res3: ErrOr[Int] = Invalid(Invalid int.Invalid boolean.)
197+
* }}}
198+
*/
199+
def productL[B](fb: F[B])(implicit F: Apply[F]): F[A] = F.productL(fa)(fb)
200+
201+
/**
202+
* Alias for [[productR]].
203+
*/
204+
def *>[B](fb: F[B])(implicit F: Apply[F]): F[B] = F.*>(fa)(fb)
205+
206+
/**
207+
* Alias for [[productL]].
208+
*/
209+
def <*[B](fb: F[B])(implicit F: Apply[F]): F[A] = F.<*(fa)(fb)
210+
211+
/**
212+
* @see [[Apply.map2]].
213+
*
214+
* Example:
215+
* {{{
216+
* scala> import cats.syntax.all._
217+
*
218+
* scala> val someInt: Option[Int] = Some(3)
219+
* scala> val noneInt: Option[Int] = None
220+
* scala> val someLong: Option[Long] = Some(4L)
221+
* scala> val noneLong: Option[Long] = None
222+
*
223+
* scala> someInt.map2(someLong)((i, l) => i.toString + l.toString)
224+
* res0: Option[String] = Some(34)
225+
*
226+
* scala> someInt.map2(noneLong)((i, l) => i.toString + l.toString)
227+
* res0: Option[String] = None
228+
*
229+
* scala> noneInt.map2(noneLong)((i, l) => i.toString + l.toString)
230+
* res0: Option[String] = None
231+
*
232+
* scala> noneInt.map2(someLong)((i, l) => i.toString + l.toString)
233+
* res0: Option[String] = None
234+
* }}}
235+
*/
236+
def map2[B, C](fb: F[B])(f: (A, B) => C)(implicit F: Apply[F]): F[C] =
237+
F.map2(fa, fb)(f)
238+
239+
/**
240+
* @see [[Apply.map2Eval]].
241+
*
242+
* Example:
243+
* {{{
244+
* scala> import cats.{Eval, Later}
245+
* scala> import cats.syntax.all._
246+
*
247+
* scala> val bomb: Eval[Option[Int]] = Later(sys.error("boom"))
248+
* scala> val x: Option[Int] = None
249+
*
250+
* scala> x.map2Eval(bomb)(_ + _).value
251+
* res0: Option[Int] = None
252+
* }}}
253+
*/
254+
def map2Eval[B, C](fb: Eval[F[B]])(f: (A, B) => C)(implicit F: Apply[F]): Eval[F[C]] =
255+
F.map2Eval(fa, fb)(f)
64256
}

core/src/main/scala/cats/syntax/contravariantMonoidal.scala

+5-3
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@ package syntax
2525
import cats.ContravariantMonoidal
2626

2727
trait ContravariantMonoidalSyntax {
28-
implicit final def catsSyntaxContravariantMonoidal[F[_], A](
29-
fa: F[A]
30-
)(implicit F: ContravariantMonoidal[F]): ContravariantMonoidalOps[F, A] =
28+
@deprecated("Kept for binary compatibility", "2.10.0")
29+
final def catsSyntaxContravariantMonoidal[F[_], A](
30+
fa: F[A],
31+
F: ContravariantMonoidal[F]
32+
): ContravariantMonoidalOps[F, A] =
3133
new ContravariantMonoidalOps[F, A] {
3234
type TypeClassType = ContravariantMonoidal[F]
3335

core/src/main/scala/cats/syntax/contravariantSemigroupal.scala

+5-3
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@ package syntax
2525
import cats.ContravariantSemigroupal
2626

2727
trait ContravariantSemigroupalSyntax extends TupleSemigroupalSyntax {
28-
implicit final def catsSyntaxContravariantSemigroupal[F[_], A](
29-
fa: F[A]
30-
)(implicit F: ContravariantSemigroupal[F]): ContravariantSemigroupal.Ops[F, A] =
28+
@deprecated("Kept for binary compatibility", "2.10.0")
29+
final def catsSyntaxContravariantSemigroupal[F[_], A](
30+
fa: F[A],
31+
F: ContravariantSemigroupal[F]
32+
): ContravariantSemigroupal.Ops[F, A] =
3133
new ContravariantSemigroupal.Ops[F, A] {
3234
type TypeClassType = ContravariantSemigroupal[F]
3335

core/src/main/scala/cats/syntax/semigroupal.scala

+40-1
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,52 @@ package cats
2323
package syntax
2424

2525
trait SemigroupalSyntax {
26-
implicit final def catsSyntaxSemigroupal[F[_], A](fa: F[A])(implicit F: Semigroupal[F]): SemigroupalOps[F, A] =
26+
@deprecated("Use `catsSyntaxSemigroupalOps2`", "2.10.0")
27+
final def catsSyntaxSemigroupal[F[_], A](fa: F[A], F: Semigroupal[F]): SemigroupalOps[F, A] =
2728
new SemigroupalOps[F, A] {
2829
type TypeClassType = Semigroupal[F]
2930

3031
val self = fa
3132
val typeClassInstance = F
3233
}
34+
35+
implicit def catsSyntaxSemigroupalOps2[F[_], A](fa: F[A]): SemigroupalOps2[F, A] =
36+
new SemigroupalOps2[F, A](fa)
37+
38+
}
39+
40+
final class SemigroupalOps2[F[_], A](private val fa: F[A]) extends AnyVal {
41+
42+
/**
43+
* @see [[Semigroupal.product]]
44+
*
45+
* Example:
46+
* {{{
47+
* scala> import cats.syntax.all._
48+
*
49+
* scala> val noneInt: Option[Int] = None
50+
* scala> val some3: Option[Int] = Some(3)
51+
* scala> val noneString: Option[String] = None
52+
* scala> val someFoo: Option[String] = Some("foo")
53+
*
54+
* scala> noneInt.product(noneString)
55+
* res0: Option[(Int, String)] = None
56+
*
57+
* scala> noneInt.product(someFoo)
58+
* res1: Option[(Int, String)] = None
59+
*
60+
* scala> some3.product(noneString)
61+
* res2: Option[(Int, String)] = None
62+
*
63+
* scala> some3.product(someFoo)
64+
* res3: Option[(Int, String)] = Some((3,foo))
65+
* }}}
66+
*/
67+
def product[B](fb: F[B])(implicit F: Semigroupal[F]): F[(A, B)] = F.product(fa, fb)
68+
69+
@deprecated("Replaced by an apply syntax, e.g. instead of (a |@| b).map(...) use (a, b).mapN(...)", "1.0.0-MF")
70+
def |@|[B](fb: F[B]): SemigroupalBuilder[F]#SemigroupalBuilder2[A, B] =
71+
new SemigroupalBuilder[F] |@| fa |@| fb
3372
}
3473

3574
abstract class SemigroupalOps[F[_], A] extends Semigroupal.Ops[F, A] {

tests/shared/src/test/scala/cats/tests/SyntaxSerializationSuite.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ package cats.tests
2323

2424
import cats.laws.discipline.SerializableTests
2525
import cats.syntax.either._
26+
import cats.Semigroupal
2627

2728
/**
2829
* Test that our syntax implicits are serializable.
@@ -36,7 +37,7 @@ class SyntaxSerializationSuite extends CatsSuite {
3637
)
3738

3839
checkAll("SemigroupalOps[Option, Int]",
39-
SerializableTests.serializable(cats.syntax.all.catsSyntaxSemigroupal[Option, Int](None))
40+
SerializableTests.serializable(cats.syntax.all.catsSyntaxSemigroupal[Option, Int](None, Semigroupal[Option]))
4041
)
4142

4243
checkAll(

tests/shared/src/test/scala/cats/tests/SyntaxSuite.scala

+23
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,8 @@ object SyntaxSuite {
368368
val f = mock[(A, B, C) => Z]
369369
val ff = mock[F[(A, B, C) => Z]]
370370

371+
fa.productR(fb)
372+
fa.productL(fb)
371373
fa *> fb
372374
fb <* fc
373375

@@ -394,6 +396,19 @@ object SyntaxSuite {
394396

395397
thabcde.imapN(f5)(g5)
396398
(ha, hb, hc, hd, he).imapN(f5)(g5)
399+
400+
val tfab = mock[F[A => B]]
401+
tfab.ap(fa)
402+
tfab <*> fa
403+
404+
val tabcf = mock[F[(A, B) => C]]
405+
tabcf.ap2(fa, fb)
406+
407+
val tabc = mock[(A, B) => C]
408+
fa.map2(fb)(tabc)
409+
410+
val tEvalfb = mock[Eval[F[B]]]
411+
fa.map2Eval(tEvalfb)(tabc)
397412
}
398413

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

689704
val result: Option[List[B]] = list.traverseCollect(f)
690705
}
706+
707+
def testSemigroupal[F[_]: Semigroupal, A, B]: Unit = {
708+
val fa = mock[F[A]]
709+
val fb = mock[F[B]]
710+
711+
fa.product(fb)
712+
fa |@| fb
713+
}
691714
}

0 commit comments

Comments
 (0)