-
-
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 groupByNelMap
method to List
syntax
#4215
base: main
Are you sure you want to change the base?
Conversation
I agree that such a method could be a really nice addition. A couple of thoughts though:
|
I've chosen |
Actually, there are a bunch of methods in Cats already that use just
So to me it seems that the method you're proposing should be named as just UPD. Just realized that it will conflict with Also IMO it would be better to have the "original" implementation created for |
Yes sure, totally up to you. I mean, it would be really nice to reach some consistency across all the syntaxes eventually. |
@satorg Thanks for your thoughts on this, but |
Edit: nope, ignore me. I must be confusing with some other collections, sorry. |
Sure, np. Just to emphasize my concern on the naming: I'm only striving to retain some degree of consistency across method names. So to me it seems that So yeah, not really sure whether such a name would be intuitive or not, just seems to me more consistent across the board. |
This one seems good for 2.9.0. If only we can agree on a name 😅 |
@armanbilge yeah, as it's well discovered, there are only two hard things in programming: cache invalidation and naming things. |
Let me enumerate all group-like methods we have both in Scala library and Cats (just for everyone's convenience).
|
I would say, there are at least two takeaways from the table:
p.2 is what I am voting for in this PR's discussion – looks like there's some convention established and I think it would be better to follow the convention. |
And one more thing I didn't reflect in the table, but which does concern me as well: all existing def groupMapNem[K, B](key: A => K)(f: A => B)(implicit K: Order[K]): NonEmptyMap[K, NonEmptyList[B]] =
NonEmptyMap.fromMapUnsafe(groupMap(key)(f)) Whereas the suggested method |
As the author of a method with a super controversial name that survived four months of bikeshedding discussion in his PR, here's my 2 cents. def groupMapNel[K, B](key: A => K)(f: A => B)(implicit K: Order[K]): SortedMap[K, NonEmptyList[B]] If you don't consider the sorting aspect of the method (i.e. requiring an P.S. @satorg 's table seems really like an invitation to implement all the remaining |
I would say, it would be really nice to have all the method sets unified across the board eventually. But before you start digging into it, could take a look at #3998 please? In particular, it already includes some of the missing methods from the table, but the discussion is the most interesting part of it, I think. The PR is market as "Draft" now although at the time it was created I assumed it is ready-to-go. Nevertheless, I switched it to Draft because I appreciated comments from @johnynek. Just because Cats is more about typeclasses and generalization rather than some set of handy collections. @danicheg @armanbilge you guys are also welcome 😉 |
This is very interesting. Generalizing over collections has become an interest of mine as well, see #4245 and #4246 for example. Along those lines, it seems to me that we could implement Edit: ah, I see this idea is exactly proposed in #3998 (comment). And perhaps we don't need a parallel-like thing after all. Perhaps all of this deserves a discussion. |
But why |
Fair point, I forgot about |
It regards to the generalization... Let me expand a little bit more on that with an example: Frankly speaking, having two separate methods in Cats that have similar functionality but only different in their return types is not cool at all. Because apparently, there are questions following: should we add Crearly, it would be a huge lack of generalization in Cats. Some Java libraries do appreciate such an approach, but I don't think that Cats should :) |
Good question, and honestly, I don't know :) |
so, it seems like what we really want is something like: trait HasNonEmpty[F[_]] {
type NonEmpty[_]
def neSemigroupK: SemigroupK[NonEmpty]
def monoidK: MonoidK[F]
def neTraverse: NonEmptyTraverse[NonEmpty]
def traverse: Traverse[F]
/**
* obvious laws here: toNonEmpty(monoidK.empty) == None
* toNonEmpty(f).map(fromNonEmpty) == Some(f) unless f is monoidK.isEmpty
*/
def toNonEmpty[A](f: F[A]): Option[NonEmpty[A]]
def fromNonEmpty[A](ne: NonEmpty[A]): F[A]
}
object GroupBy {
def groupByNeOrd[F[_], A, B: Order, C](f: F[A])(key: A => B)(value: A => C)(implicit hne: HasNonEmpty[F]): SortedMap[B, hne.NonEmpty[C])
} |
That's what I was imagining at first as well, but I wonder if we can be more general and collect into any def groupByNeOrd[F[_], G[_]: NonEmptyAlternative, A, B: Order, C](f: F[A])(key: A => B)(value: A => C): SortedMap[B, G[C]] |
yes, I think so, but I worry about being forced through the append/prepend interface which means you have to construct immutably and pick a direction (append or prepend), which I think will hurt performance. Alternatively, we could possibly add this method to |
That is an interesting idea... We can definitely give it a try. Then, having Let me try to sketch it out: trait NonEmptyAlternative[F[_]] {
// With Foldable we can only group into a regular Map/SortedMap
// Suffix "To" is used to differentiate from regular groupBy which groups into the same collection type as the source is.
def groupByTo[G[_]: Foldable, K: Order, A](ga: G[A])(kf: A => K): SortedMap[K, F[A]]
// With Reducible we can group into `NonEmptyMap`
// Suffix "To" is used to differentiate from regular groupBy which groups into the same collection type as the source is.
def groupByToNem[G[_]: Reducible, K: Order, A](ga: G[A])(kf: A => K): NonEmptyMap[K, F[A]]
}
// And now the syntax
object Foldable {
trait Ops[F[_], A] {
def self: F[A]
def groupByTo[G[_]: NonEmptyAlternative, K: Order](kf: A => K): SortedMap[K, G[A]]
}
}
object Reducible {
trait Ops[F[_], A] {
def self: F[A]
def groupByToNem[G[_]: NonEmptyAlternative, K: Order](kf: A => K): NonEmptyMap[K, G[A]]
}
} Something like that? |
The only issue I see here is that when using such an approach, a user will be forced to specify not only a desired output collection type (which is required here), but also all the other generic parameters (
which could be inconvenient to use. An apparent solution for that could be an intermediate apply-like class:
..or something like that... Looks tricky, but might be working... Wdyt? |
I find this method extremely helpful in my day job. And since there is
List
syntax, it seems natural for me to have this method added.