-
-
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
Cats Order needs an orElseBy
combinator
#4621
Comments
Seems like a good PR. I don't know all the motivations for Order but I think it was that Ordering I believe was contravariant and scala had (or maybe has?) some issues with very surprising implicit resolution on contravariant type classes. |
I think it would be a great addition indeed. However, I wonder why |
What would be the semantics of That semantic does seem workable . Perhaps the slight ambiguity, or simply that PartialOrdering is less commonly used, is the reason. |
That's right. So I think that in theory, Not sure if all that makes sense though, wdyt? |
A precedent for Partial order
I'm reluctant to implement a
In spirit, yes. But partial orders return a comparison wrapped in an Option, while total orders omit the Option. Interestingly, ZIO nicely avoids an Option by using a 4-branch sealed trait (LT, Eq, GT, Incomparable), which nests a 3-branch trait used for total orders 🆒 |
Actually, in Cats the cats/kernel/src/main/scala/cats/kernel/PartialOrder.scala Lines 59 to 80 in dd89133
So it still should be quite efficient. |
Agree. I'm just trying to poke around different possibilities and outcomes. For example, if a method can be added to a base trait, it is usually better to add it there from the beginning even if it is not needed right away. Because if we decided to add it to the base class later, then it would be more difficult to overcome inevitable binary compatibility issues. But when it comes to |
A possible fix for this provides a nice parallel with the existing method: name it This seems to imply fallbacks in any ambiguous case (equal or incomparable): PartialOrdering
.by[Single, VPNTier](_.tier)
.orElseBy(it => (it.lang1, it.lang2))
.orElseBy(_.num)
.orElseBy(_.postfix) This doesn't have that connotation (at least to my reading): PartialOrdering
.by[Single, VPNTier](_.tier)
.whenEqual(it => (it.lang1, it.lang2))
.whenEqual(_.num)
.whenEqual(_.postfix) |
Leaving aside the question of whether
cats.Order
existing (on top ofscala.math.Ordering
on top ofjava.util.Comparator
) was wise decision 🙄 , at least it ought to be greaterThanOrEqual to its predecessor.But alas, it lacks something valuable that Ordering has; a convenient syntax to construct a n-level hierarchical ordering for a record, by delegating to the orderings of several record fields. For example, if we have a
case class Person(name: String, age: Int)
we might wish to order byage
but if ages are equal, usename
as a discriminator. This is super common IME. Eg SQL has built in syntax for itorder by age, name
.There are at least two attempts at addressing this in cats.Order, but alas both are less convenient than the (pre-existing) combinator in
scala.math.Ordering
:def orElseBy[S](f: T => S)(implicit ord: Ordering[S]): Ordering[T]
Hence we find code like this in the wild:
The attempts in cats.Order are:
Order.whenEqual[Person](Order.by(_.age), Order.by(_.name))
. Well it's already looking slightly awkward, but what if we wanted to add a 3rd discriminator? We'd need to nest it inside the second arg with another call to Order.whenEqual. Confusing right nesting.whenEqual
Monoid is beautiful and tantalizes us with the promise ofval o: Order[Person] = Order.by(_.age) |+| Order.by(_.name)
. But I could not get the types to infer properly even on Scala 3.5 RC1.If it ain't broke, don't fix it.
orElseBy
does the job well and should be brought into Cats.The text was updated successfully, but these errors were encountered: