Cats Order needs an `orElseBy` combinator
Nobody has claimed this yet.
- Dominant language
- Scala
- Stars
- 5.5k
- Forks
- 1.2k
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 5
Description
Leaving aside the question of whether cats.Order existing (on top of scala.math.Ordering on top of java.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 by age but if ages are equal, use name as a discriminator. This is super common IME. Eg SQL has built in syntax for it order 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:
given Order[Single] = Order.fromOrdering(
Ordering
.by[Single, VPNTier](_.tier)
.orElseBy(it => (it.lang1, it.lang2))
.orElseBy(_.num)
.orElseBy(_.postfix)
)
The attempts in cats.Order are:
- code.
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. - code. The
whenEqualMonoid 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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in cats/kernel/src/main/scala/cats/kernel/Order.scala, around the existing whenEqual methods, and compare scala.math.Ordering.orElseBy. Check how a chained API handles a third discriminator and Scala 3 type inference. Done means Cats offers an orElseBy-style combinator that supports the Person age/name example and preserves the intended ordering behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- scala
- Domain
- developer-experience
- Issue type
- Feature
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100