-
-
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
Add filterFold and collectFold to Foldable #2452
Add filterFold and collectFold to Foldable #2452
Conversation
def filterFold[A, M: Monoid](fa: F[A])(f: A ⇒ Option[M]): M = { | ||
val m = Monoid[M] | ||
foldLeft(fa, m.empty)((acc, a) ⇒ f(a) match { | ||
case Some(x) ⇒ m.combine(x, acc) |
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 think the order on combine is wrong. I think this won’t match collectFold for a non commutative Monoid.
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.
Yes, that's right, thanks, I will fix it soon :)
test(s"Foldable[$name] partial summation") { | ||
forAll { (fa: F[Int], f: Int ⇒ Boolean) ⇒ | ||
val m: Monoid[Int] = Monoid[Int] | ||
|
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.
Let’s test with a non commutative Monoid like String.
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.
Yes, l will do it, thanks.
The CI complains an binary compatibility issue: [error] * method filterFold(java.lang.Object,scala.Function1,cats.kernel.Monoid)java.lang.Object in trait cats.Foldable is present only in current version
[error] filter with: ProblemFilters.exclude[ReversedMissingMethodProblem]("cats.Foldable.filterFold")
[error] * method collectFold(java.lang.Object,scala.PartialFunction,cats.kernel.Monoid)java.lang.Object in trait cats.Foldable is present only in current version
[error] filter with: ProblemFilters.exclude[ReversedMissingMethodProblem]("cats.Foldable.collectFold")
[error] * method filterFold(scala.Function1,cats.kernel.Monoid)java.lang.Object in trait cats.Foldable#Ops is present only in current version
[error] filter with: ProblemFilters.exclude[ReversedMissingMethodProblem]("cats.Foldable#Ops.filterFold")
[error] * method collectFold(scala.PartialFunction,cats.kernel.Monoid)java.lang.Object in trait cats.Foldable#Ops is present only in current version
[error] filter with: ProblemFilters.exclude[ReversedMissingMethodProblem]("cats.Foldable#Ops.collectFold") Should I implement these two methods as syntax extensions to avoid this problem? |
Codecov Report
@@ Coverage Diff @@
## master #2452 +/- ##
=========================================
+ Coverage 95.29% 95.39% +0.1%
=========================================
Files 351 357 +6
Lines 6371 6519 +148
Branches 279 275 -4
=========================================
+ Hits 6071 6219 +148
Misses 300 300
Continue to review full report at Codecov.
|
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 think it looks good for now, I'd like to add more laws like the one I mentioned in the issue, but since we can't add these two methods to the type class I'm more than content to just merge this as is :)
* res0: Int = 6 | ||
*}}} | ||
*/ | ||
def filterFold[M: Monoid](f: A ⇒ Option[M])(implicit F: Foldable[F]): M = { |
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.
def filterFold[M: Monoid](f: A ⇒ Option[M])(implicit F: Foldable[F]): M =
collectFold(Function.unlift(f))
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.
or, the other way around:
def collectFold[M: Monoid](f: PartialFunction[A, M])(implicit F: Foldable[F]): M =
filterFold(f.lift)
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.
We could indeed define these in terms of each other. I'm not sure if they'd be more performant than just using foldLeft
, but if we do this, then it should be filterFold
in terms of collectFold
, since A => Option[M]
has to allocate an extra Option
:)
Up to you, @satansk :)
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.
Hi @PeterPerhac @LukaJCB , I think define them in terms of each other is good, but people are more familiar with foldLeft
, so I personally prefer to define them by foldLeft
:)
But if there are big performance differences, we can change to the better one.
*}}} | ||
*/ | ||
def collectFold[M: Monoid](f: PartialFunction[A, M])(implicit F: Foldable[F]): M = { | ||
val m = Monoid[M] |
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.
totally nitpick - there are two styles of implicit type class instance summoning in this one method (and the filterFold
method below). I think it would help readability a little bit if we just use (implicit F: Foldable[F], M: Monoid[M])
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 is a nice advice, it will be more readable, and I'll fix it :)
Thanks!
* res0: Int = 6 | ||
*}}} | ||
*/ | ||
def filterFold[M](f: A ⇒ Option[M])(implicit F: Foldable[F], M: Monoid[M]): M = |
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.
The word filter
, to me, kind of implies using a boolean value.
We already have a collectFirstSome
https://github.com/typelevel/cats/blob/master/core/src/main/scala/cats/Foldable.scala#L233
how about naming this one collectSomeFold
?
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.
A reasonable advice, I'll fix it.
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.
We do have mapFilter
which does use Option
though.
Maybe this should be mapFilterFold
? I don't know
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.
Right, I am fine with mapFilterFold
too. Sorry about the bikeshedding @satansk , your call.
👍 |
@satansk +100 :) |
@hepin1989 :) |
fix #2322