scalatraitsenumerationcomparablepartial-ordering

# How to do ordering of a sealed trait?

I have a distributed system defined by a sort of "state machine" ( "flow chart" )

each system writes there state in a shared "log"

I'm representing each state as part of a sealed trait and a given "status" for that state

I want to "merge/reduce" to a single status which represents the current progress.

(There are some relaxations in place as not all must succeed in order for the final status to complete successfully)

There are 2 sealed traits representing the flow:

``````sealed trait System
case object A extends System
case object B extends System
case object C extends System
...

sealed trait Status
case object Pending extends Status
case object InProgress extends Status
case object Success extends Status
case object Fail extends Status
``````

Log:

``````A, Success
B, Fail
C, Pending
...
...
``````

now there are a set of rules which I use to define a single status reduction

basically it gives a priority

`A < B < C, ... < Z`

and

`Pending < InProgress < Success < Fail`

so if there is a status of:

`(A, Success)` versus `(C, Pending)`

I want to reduce it to `(C,Pending)`

and if

`(A,Success)` versus `(B, Fail)`

I want to reduce it to `(B, Fail)`

I can model this as a simple integer comparison in my case (possibly with an outlier which I explicitly test for)

I'm not clear how to make the sealed traits comparible/orderable which would make my life way easier

something along these lines is adequate:

``````def reduce(states: Seq[(System,Status)]) : (System,Status) = {
states.order... {left.system < right.system) && (a.status < b.status) ... possibly another ordering test ....}.tail // take the last one in the ordering
}
``````

Solution

• You can define a `scala.math.Ordering[Status]`:

``````object StatusOrdering extends Ordering[Status] {
def compare(x: Status, y: Status): Int =
(x, y) match {
// assuming that the ordering is Pending < InProgress < Success < Fail...
case (_, _) if (x eq y) => 0
case (Pending, _) => -1
case (_, Pending) => 1
case (InProgress, _) => -1
case (_, InProgress) => 1
case (Success, _) => -1
case (_, Success) => 1
case _ => 0 // (Fail, Fail)
}
``````

In your `reduce`, you can then

``````import StatusOrdering.mkOrderingOps
``````

and your `Status` objects will be enriched with `<` and friends.

It is also possible to have your `trait` extend `Ordered[Status]`, which defines a canonical ordering in the trait:

``````sealed trait OrderedStatus extends Ordered[OrderedStatus] {
def compare(that: OrderedStatus): Int =
(this, that) match {
case (x, y) if (x eq y) => 0
case (Qux, _) => -1
case (_, Qux) => 1
case (Quux, _) => -1
case (_, Quux) => 1
case _ => 0
}
}

case object Qux extends OrderedStatus
case object Quux extends OrderedStatus
case object Quuux extends OrderedStatus
``````

Then you don't have to import `mkOrderingOps`, but I personally dislike the forward use of the extending `case objects` in the `compare` method (and the alternative of a boilerplate `compare` in each case object is even worse).