In this post we’ll look at how to implement ordering using Scalaz Order
trait. However, before we do that, let’s step back a little and look at how we implement ordering using scala.math.Ordering
Sorting a Collection - Scala Way
Say we have a List
and we’d like to sort it.[1] We can do so by calling the sorted
method on it and it’ll work out-of-the-box for lists of Int
, Float
, etc. because there is an implicit in scala.math.Ordering
.
1 | @ List(1, 2, 3, 5, 4, 10, -1, 0).sorted |
Going over the documentation for scala.math.Ordering
[2]:
Ordering is a trait whose instances each represent a strategy for sorting instances of a type.
Ordering’s companion object defines many implicit objects to deal with subtypes of AnyVal (e.g. Int, Double), String, and others.
All the implicit objects in the companion object implement the scala.math.Ordering
trait which extends the java.util.Comparator
interface. Thus, they all have a compare
method. What if we have types for which there is no sorting strategy in the companion object of scala.math.Ordering
? We define our own like so:
1 | @ case class Salary(amt: Float) |
But we still don’t have operators like <
, <=
, etc. on type Salary
.
1 | @ Salary(999.0f) > Salary(555.0f) |
To do that, the trait scala.math.Ordered
would have to be mixed in.
1 | @ case class Salary(amt: Float) extends Ordered[Salary] { |
And we know that if we’re working with a library, we do not have the liberty to mix a trait for our convenience. Also, Scala’s comparison operators are not type safe.
1 | @ 1 > 2.0 |
Sorting a Collection - Scalaz Way
As stated before, Scalaz provides Order
trait. Staying with our Salary
example, let’s put Scalaz Order
to use.
1 | @ import scalaz._ |
We’ve defined a way to order Salary
objects using Scalaz Order
trait via a typeclass. The implicit object needs to provide an implementation for order
method which returns an Ordering
value. Going over the code for Scalaz Ordering
:
This Ordering is analogous to the Ints returned by scala.math.Ordering.
Now, we have comparison operators at our disposal. Here’s how we can compare Salary
objects using the ?|?
operator:
1 | @ Salary(1.0f) ?|? Salary(2.0f) |
?|?
can also be used with Int
, Float
, etc. and is type-safe.
1 | @ 1.0 ?|? 2.0 |
Also, analogous to <
, <=
, we now have type-safe lt
, lte
, etc.
1 | @ 1.0 gte 2.0 |
That’s not where the power ends. You can also compare Option
s seamlessly. Do note that it is some
with a lowercase “s”.
1 | @ some(Salary(1.0f)) ?|? some(Salary(2.0f)) |
You can now also use sorted
on a collection equally easily. Using Order[A].toScalaOrdering
we can get back an object of type scala.math.Ordering
which we can use to sort collections.
1 | @ implicit val salaryOrdering = Order[Salary].toScalaOrdering |
Also, since Order
trait extends the Equal
trait, you get the ===
operator, too.
1 | @ Salary(0.0F) === Salary(1.0F) |
Conclusion
By using Scalaz Order
trait, we can implement comparison between values in a type-safe and extensible way. There’s seamless transformation from Scalaz Order
to scala.math.Ordering
which lets us use the sorted
method on collections.