-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Fix unnecessary syntax allocation #4477
Conversation
I plan to make modifications for syntax other than |
Co-authored-by: Arman Bilge <armanbilge@gmail.com>
implicit final def catsSyntaxProductOps[F[_], A, B](fa: F[A]): ProductOps[F, A, B] = | ||
new ProductOps[F, A, B](fa) | ||
|
||
implicit final def catsSyntaxMap2Ops[F[_], A, B, C](fa: F[A]): Map2Ops[F, A, B, C] = | ||
new Map2Ops[F, A, B, C](fa) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why separate these ones?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no strong reason to separate them, so I can put these together. (but it was a bit difficult to come up with a unique syntax name that doesn't conflict with exisisting one. ApplySyntaxFA
or ApplyOps2
would be fine?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ApplyOps2
or ApplyOpsBincompat1
or similar seems fine. We probably have some similar situations somewhere else in Cats.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix: f97affd
implicit final def catsSyntaxProductOps[F[_], A, B](fa: F[A]): ProductOps[F, A, B] = | ||
new ProductOps[F, A, B](fa) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I doubt that type inference would work out well in this case. An implicit method should only include types that are parts of its parameters. Otherwise the compiler may not have a clue how to infer them. I would suggest
implicit final def catsSyntaxProductOps[F[_], A, B](fa: F[A]): ProductOps[F, A, B] = | |
new ProductOps[F, A, B](fa) | |
implicit final def catsSyntaxProductOps[F[_], A](fa: F[A]): YouNameIt[F, A] = | |
new YouNameIt[F, A](fa) |
then in the YouNameIt
:
final class YouNameIt[F[_], A](private val fa: F[A]) extends AnyVal {
def productR[B](fb: F[B])(implicit F: Apply[F]): F[B] = F.productR(fa)(fb)
}
I.e. the B
type is introduced when it is actually used.
And the same for the other implicit methods below.
Feel free to give a try to your versions of course, but it looks suspicious to me personally.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And of course all the methods that require just F[A]
can be grouped together. Only methods that require more specialized types like F[A => B]
have to be extracted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I didn't consider that aspect, thank you for pointing it out.
Fix: 0ee124e
@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 |
There was a problem hiding this comment.
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.
I also tried to address cats/core/src/main/scala/cats/ContravariantSemigroupal.scala Lines 59 to 63 in 7da28b4
cats/core/src/main/scala/cats/ContravariantMonoidal.scala Lines 65 to 69 in 7da28b4
Or should I deprecate the syntax that causes allocation and just add a new empty syntax class for future use? |
If there's no actual syntax that can be used then I would suggest simply to deprecate it. |
Co-authored-by: Sergey Torgashov <satorg@users.noreply.github.com>
8ae9b9c
to
f09b400
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me. Very nice work and thanks for your patience!
After we merge this it is probably time to release Cats v2.10.0 :)
} | ||
|
||
private[syntax] trait ApplySyntaxBinCompat0 { | ||
implicit final def catsSyntaxIfApplyOps[F[_]](fa: F[Boolean]): IfApplyOps[F] = | ||
new IfApplyOps[F](fa) | ||
} | ||
|
||
final class ApplyFABOps[F[_], A, B](private val fab: F[A => B]) extends AnyVal with Serializable { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel we should either add with Serializable
everywhere to each *Ops
or go remove it, to be consistent.
However, this class does have it whereas the next one down below does not.
I personally don't see any reason why we would need Serializable
for such a kind of object, so I'd prefer to remove it. But maybe there is a reason, @armanbilge could you suggest please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #4477 (comment). It shouldn't be needed if the syntax classes are not being allocated, which is the hope 😅
I don't know enough of the history but at some point it seems that people did care that these were serializable /shrug
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It shouldn't be needed if the syntax classes are not being allocated
But even if those class instances are still allocated, what is the point in having them serialized whatsoever?
Those class instances are short-living and get instantiated just in place. They are not supposed to be saved nor transferred anywhere, are they?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are not supposed to be saved nor transferred anywhere, are they?
I have no idea. I think typically serializability is important when you are dealing with e.g. Spark. Maybe in some (pathological) situations these classes can get caught up in the serialization graph somehow ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 yeah, not sure either. I would probably suggest to remove Serializable
for now. Just because it is always easier to add something later rather than remove it whenever. We are not introducing any binary incompatibility anyway, so if Spark guys or anyone else start complaining, then we can easily issue a hotfix release. But at least we will know for sure in that case :) Although I'd guess there will be no complains about it.
Otherwise, if we decide to go the opposite way, then we should add Serializable
all the remaining syntax extension classes, shouldn't we?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with removing it. Anyway I think all Spark users are stuck on Cats 2.0.0-M5 or something 😛
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh sorry, I unintentionally made ApplyFABOps Serializable🤦♂️
Thanks for pointing that out.
Hey @mox692, @armanbilge, @satorg! 🙂 |
Sorry for the delay (and thank you for the ping 😄). The code itself looks great indeed, no concerns about it. However I've found out that there're no tests that could help us to make sure that the changes do actually work as expected. It could be especially important for the syntax moved into the There are syntax tests in the @mox692, wdyt? |
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 { |
There was a problem hiding this comment.
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 😄
Ouch, good catch. I agree, we should have these to protect against these kind of refactorings. |
Sure, I'll add the test too. |
Hmmm, it looks like In mox692/cats, the test seems to succeed for the same commit 288587e. ci logs
|
Sorry, looks like it was a flake. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding those tests! Ok, I think we are finally good 😁
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, looks great!
Try to fix #4475.