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

NonEmptyList.groupByNelA #3432

Merged
merged 12 commits into from
Jun 7, 2020
Merged

NonEmptyList.groupByNelA #3432

merged 12 commits into from
Jun 7, 2020

Conversation

pk044
Copy link
Contributor

@pk044 pk044 commented May 25, 2020

Closes #3420

LukaJCB
LukaJCB previously approved these changes May 25, 2020
Copy link
Member

@LukaJCB LukaJCB left a comment

Choose a reason for hiding this comment

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

This is really neat, thanks!

implicit val ordering: Ordering[B] = B.toOrdering

toNel.fold(F.pure(SortedMap.empty[B, NonEmptyList[A]]))(nel =>
F.map(nel.traverse(a => F.tupleLeft(f(a), a)))(_.groupBy(_._2).mapValues(_.map(_._1)))
Copy link
Member

Choose a reason for hiding this comment

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

mapValues was changed in the 2.13 collection rewrite, we could use its Functor instance here instead :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thank you for the suggestion! i'll push a fix soon :)

@codecov-commenter
Copy link

codecov-commenter commented May 26, 2020

Codecov Report

Merging #3432 into master will increase coverage by 0.00%.
The diff coverage is 100.00%.

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #3432   +/-   ##
=======================================
  Coverage   91.64%   91.64%           
=======================================
  Files         381      381           
  Lines        8268     8272    +4     
  Branches      227      225    -2     
=======================================
+ Hits         7577     7581    +4     
  Misses        691      691           
Flag Coverage Δ
#scala_version_212 91.63% <100.00%> (+<0.01%) ⬆️
#scala_version_213 91.43% <100.00%> (+<0.01%) ⬆️
Impacted Files Coverage Δ
core/src/main/scala/cats/syntax/list.scala 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 6f27f07...4a67159. Read the comment docs.

*
* scala> val expectedResult = Option(SortedMap(false -> NonEmptyList.of(-2, -5), true -> NonEmptyList.of(12, 3)))
*
* scala> list.groupByNelA(num => Option(0).map(num >= _)) === expectedResult
Copy link
Member

Choose a reason for hiding this comment

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

btw you don't need to do any explicit assertions here, you could just do

scala> list.groupByNelA(num => Option(0).map(num >= _))
res0: Option(SortedMap(false -> NonEmptyList(-2, -5), true -> NonEmptyList(12, 3)))

which will be a little less noise for the reader, while still asserting that the resulting expression is the same as what you typed. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hmm, this one is pretty interesting

[info] > Labels of failing property: 

[info] 'Some(TreeMap(false -> NonEmptyList(-2, -5), true -> NonEmptyList(12, 3)))' is not equal to 'Some(Map(false -> NonEmptyList(-2, -5), true -> NonEmptyList(12, 3)))'

this seems to fail only on Scala 2.13.1 🤔 works for me locally and on the rest of Scala versions https://travis-ci.org/github/typelevel/cats/builds/691476028

Copy link
Member

Choose a reason for hiding this comment

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

It works for you locally on 2.13?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

2.12.10

Copy link
Contributor Author

Choose a reason for hiding this comment

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

quite not sure how to approach this - is there anything I can do to work it out or should I just stay with the explicit assertion?

Copy link
Contributor

Choose a reason for hiding this comment

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

This is an issue with toString changing for some collection types between 2.12 and 2.13. Unfortunately I think you have to do something like this: #3155

Copy link
Contributor

@travisbrown travisbrown left a comment

Choose a reason for hiding this comment

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

I like this idea! I have two minor suggestions but think we should aim to get this into 2.2.0.

At first I wasn't 100% sure I liked the name, since there are lots of other places where we use F as a suffix even though we only have an Applicative constraint, but on second thought I think it's better to match e.g. foldMapA, filterA, etc. (and that inspectF, modifyF, etc. are the inconsistent ones).


toNel.fold(F.pure(SortedMap.empty[B, NonEmptyList[A]]))(nel =>
F.map(nel.traverse(a => F.tupleLeft(f(a), a)))(list =>
Functor[SortedMap[B, *]].map(list.groupBy(_._2))(_.map(_._1))
Copy link
Contributor

Choose a reason for hiding this comment

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

This won't generally be a big deal, I guess, but instantiating this instance repeatedly doesn't seem ideal, and it'd be easy to lift it out.

* res0: Option[SortedMap[Boolean, NonEmptyList[Int]]] = Some(Map(false -> NonEmptyList(-2, -5), true -> NonEmptyList(12, 3)))
* }}}
*/
def groupByNelA[F[_], B](f: A => F[B])(implicit B: Order[B], F: Applicative[F]): F[SortedMap[B, NonEmptyList[A]]] = {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason to order the implicit parameters this way? There are a couple of rules of thumb that would suggest reversing them (follow the declaration order of the type parameters and put instances that characterize type constructors first).

Copy link
Contributor

Choose a reason for hiding this comment

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

SIde note: I just took a closer look at this, and Cats is currently inconsistent in this respect—there are a few methods like WriterT.liftF that use the Monoid[L], Applicative[F] order for no obvious reason, but in general the codebase follows the other order, and I think we should here (unless of course there's a specific reason you did it this way).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good point, done!

@pk044
Copy link
Contributor Author

pk044 commented May 27, 2020

seems like I adjusted the PR to all the suggestions, is there anything I should do more to get this merged? :)

Copy link
Contributor

@travisbrown travisbrown left a comment

Choose a reason for hiding this comment

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

Looks good to me, thanks.

@djspiewak djspiewak merged commit 3c44fcd into typelevel:master Jun 7, 2020
@travisbrown travisbrown added this to the 2.2.0-M3 milestone Jun 17, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add effectful groupByNelA(A => F[B]): F[SortedMap[B, NonEmptyList[A]]]
5 participants