Description
The Eq
, PartialOrder
, and Order
type classes in cats-kernel
closely resemble the Equiv
, PartialOrdering
, and Ordering
from the standard library, however there are currently some differences (which I'll discuss below).
In this issue, I'd like to have a discussion on:
- why
cats-kernel
has its own version of these type classes as opposed to using the ones from the standard library - what changes we'd want to see in the standard library before choosing to (eventually) switch over to its type classes (or reasons that this would never happen)
goal (of this issue)
I don't intend for this issue to linger around forever. It can be closed when either of the following happens:
- We've accumulated enough information for me to be able to write a proposal to make the necessary changes to the standard library OR
- We've documented the reason why we think that these typeclasses should live separately in Cats
plausibility and timeline
At this point, it's unclear whether or not this change even could feasibly happen, let alone the timeline involved. I haven't yet spoken to any standard library maintainers about this; I wanted to see where Cats maintainers stood first. But if any standard library maintainers happen to be reading this, I'd love to hear your input.
For most of the differences listed below, I've given a brief summary of my thoughts on how much of a compatibility concern it might raise in the standard library.
At this point I suspect that it's too late in the Scala 2.13 lifecycle for these changes to be proposed in the standard library. I wanted to get this out there because I've been thinking about it for quite some time but have never acted on it.
problem
There's no question that Eq
, PartialOrder
, and Order
are extremely similar to Equiv
, PartialOrdering
, and Ordering
. In the past, Cats has made decisions to prefer standard library types (even when they aren't exactly how we would want them to be) to reinventing the wheel. See the Xor to Either transition for an example.
I think that looking the many conversions between Order
and Ordering
in NonEmptySet and NonEmptyMap are indicative of clunkiness in the current state of this type class duplication. In addition to feeling clunky, they incur a bit (though probably not a lot) of performance/memory overhead.
differences between Cats and standard library
I wasn't involved in the initial creation of Eq
/PartialOrder
/Order
, but I know that there was a conscious decision to not use the type classes from the standard library. Below is a list of some differences that I have observed. They are roughly ordered with what I suspect are the most significant changes at the top.
specialization
The Cats versions of these typeclasses are all @specialized
, while the standard library versions are not. While @non would be the expert here (and for several other points below), I suspect that not using specialization for these type-classes makes using them in tight computational loops prohibitively expensive due to all of the allocation overhead of boxing.
I'm guessing that we'd need to see specialization of these type classes in the standard library to consider using them. I don't know how open to this change the standard library authors would be.
PartialOrder: Double vs Option[Int]
In the standard library's PartialOrdering
, tryCompare
returns an Option[Int]
, while in Cats' PartialOrder
, partialCompare
returns a Double
. Again, I'd defer to @non, but I would imagine that this is to avoid allocation overhead.
I'm guessing that performance-conscious people using partialCompare
would want to see the Double
-version added to the PartialOrdering
type class, which would be binary-incompatible before Scala 2.12 but seems to be like it wouldn't be a very controversial change after that.
universal Equiv
In the standard library, there is a universal Equiv instance in the Equiv
companion object. To me, this seems to repeat the problems of universal equality in Java and undermines the usefulness of type class-based equality.
I think that in order for Cats to use Equiv
, this instance would need to be removed. In theory, this is a very breaking change. In practice, I've used Scala for years in many different contexts and I've never seen Equiv
used, so I suspect that this wouldn't have a huge impact on the community. One could always provide an import for those who want this in scope.
machinist
Cats uses machinist to provide zero-cost syntax for these type classes, while the standard library does not. I know that this is supposed to provide a measurable performance improvement, but I can't speak to the magnitude or whether people who are performance-conscious could "bolt" machinist on somewhere outside of the standard library. Unfortunately, I think that there are very few people who understand machinist well (see this unanswered Cats comment from a year ago). Hopefully @non can weigh in.
comparator/comparable
In the standard library there are implicit conversions from Comparable
and Comparator
to Ordering
, while in Cats there is only an explicit conversion from Comparable
to Order
. Some might view these implicit conversions as questionable, but I don't know of a concrete reason that they should be avoided.
Comparison
Cats has a Comparison enum-like type (GreaterThan
, Equal
, or LessThan
) and Order
exposes a method that compares to items with a Comparison
result. Currently as far as I know an equivalent type doesn't exist in the standard library.
Starting with Scala 2.12, Comparison
and the associated methods could be added to the standard library in a backwards-compatible way. Even if they weren't added, I suspect that people wouldn't consider the lack of Comparison
to be a dealbreaker.