Add traverseCollect to TraverseFilter typeclass - #4277
Conversation
armanbilge
left a comment
There was a problem hiding this comment.
Thanks for the PR, looking good! As next steps, do you think we can add a law, syntax (and test for the syntax)?
|
I added syntax and test for it, but I am not sure if we need laws. Looking at |
Oh that's interesting. I think laws are good to have, in case an implementation overrides this method, and at the very least to exercise this code. It's also often good to have some tests with a concrete implementation testing it works as expected. Here's a recent PR that is a great example: |
|
By laws you mean we want to have a test proving that |
traverseCollect to TraverseFilter typeclass
|
The law should verify that the actual implementation of the method matches the default implementation—you can reuse the same code. You might find the commentary in #4248 (comment) helpful :) let me know if you have questions! |
|
I added all necessary stuff (I believe so) :) |
|
thanks for your PR! |
| package cats | ||
|
|
||
| import cats.data.State | ||
| import cats.implicits.toTraverseOps |
There was a problem hiding this comment.
Can we write this without the ops import? Current practice is try to avoid circular dependencies between syntax and typeclasses.
There was a problem hiding this comment.
I tried to do manual sequence for this operation. WDYT?
| */ | ||
| def traverseCollect[G[_], A, B](fa: F[A])(f: PartialFunction[A, G[B]])(implicit G: Applicative[G]): G[F[B]] = | ||
| traverseFilter(fa)(a => f.lift(a).sequence) | ||
| traverseFilter(fa)(a => f.lift(a).map(G.map(_)(Option(_))).getOrElse(G.pure(None))) |
There was a problem hiding this comment.
I think sequence was good, we just can't use the syntax :)
| traverseFilter(fa)(a => f.lift(a).map(G.map(_)(Option(_))).getOrElse(G.pure(None))) | |
| traverseFilter(fa)(a => Traverse[Option].sequence(f.lift(a))) |
There was a problem hiding this comment.
actually, while we are at it:
val optF = f.lift
traverseFilter(fa)(a => Traverse[Option].sequence(optF(a)))so we don't reallocate f.lift on every item a.
There was a problem hiding this comment.
@emilhotkowski I think Oscar's suggestion still applies :) it's a good optimization.
Co-authored-by: Arman Bilge <armanbilge@gmail.com>
| implicit def toTraverseCollectOps[F[_], G[_], A](fa: F[A]): TraverseCollectOps[F, G, A] = | ||
| new TraverseCollectOps(fa) |
There was a problem hiding this comment.
Should this just be TraverseFilterOps? We can add more ops in the future; it's not specific to traverseCollect I think.
There was a problem hiding this comment.
I followed current implementations like new SequenceFilterOps(fgoa) I don't know what would be the other solution tbh
There was a problem hiding this comment.
Ah the situation for SequenceFilter is slighly different, since it only works for F[G[Option[A]]], not F[A]
There was a problem hiding this comment.
Do you know where I could find examples to follow? I am not sure what I need to change here.
There was a problem hiding this comment.
Sure, check out any of the syntax implementations :)
https://github.com/typelevel/cats/tree/main/core/src/main/scala/cats/syntax
I'm just proposing that we rename TraverseCollectOps to TraverseFilterOps so that it matches the name of the typeclass and not the operation.
There was a problem hiding this comment.
I understand your point now. I thought it was a remark about implementation details. Thanks for your work on this PR !
There was a problem hiding this comment.
Ah, sorry I wasn't so clear on that. Great, thank you too!
…cala Co-authored-by: Arman Bilge <armanbilge@gmail.com>
Thank you for contributing to Cats!
This is a kind reminder to run
sbt +prePRand commit the changed files, if any, before submitting.