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
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.