davidrpugh
davidrpugh

Reputation: 4573

Defining an Ordering over subtypes in Scala

I am a complete Scala and Akka newbie, and am teaching myself the language by building an order book for equity trading. The core of my order book will be two PriorityQueues: one for bids and one for asks.

val bids = new mutable.PriorityQueue[Bid]()(BidOrdering)
val asks = new mutable.PriorityQueue[Ask]()(AskOrdering)

My idea is to define special BidOrdering and AskOrdering for these queues so that price and time priority will be maintained for bids and asks of various types. However I am having trouble defining the Ordering classes.

Here is an example (adapted from the scala docs) that demonstrates the problem more clearly...

import scala.util.Sorting

case class LimitOrderBid(quantity:Long, limit:Double)
val bids = Array(LimitOrderBid(100, 30.0), LimitOrderBid(10, 32.3), LimitOrderBid(1, 19))

// sort by limit price
object BidOrdering extends Ordering[LimitOrderBid] {
  def compare(a:LimitOrderBid, b:LimitOrderBid) = a.limit compare b.limit
}
Sorting.quickSort(bids)(BidOrdering)

The above works for bids of type LimitOrder. However my issue is that there will be many types of Bid in my model and I want to define BidOrdering in such a way that it could be used to order collections of various sub-types of Bids. I think it would look something like...

object BidOrdering extends Ordering[Bid] {
      def compare(first:Bid, second:Bid) = first compare second
    }
}

Upvotes: 3

Views: 547

Answers (1)

Travis Brown
Travis Brown

Reputation: 139038

You're on the right track—you just need to describe how to compare the different combinations of subtypes. For example:

sealed trait Base
case class Foo(i: Int) extends Base
case class Bar(s: String) extends Base
case class Qux(f: Double) extends Base

object Base {
  implicit object orderingBase extends Ordering[Base] {
    def compare(first: Base, second: Base) = (first, second) match {
      // First describe how to compare pairs of the same type
      case (Foo(i1), Foo(i2)) => i1 compare i2
      case (Bar(s1), Bar(s2)) => s1 compare s2
      case (Qux(f1), Qux(f2)) => f1 compare f2

      // Every Foo precedes any non-Foo
      case (Foo(_), _) => -1
      case (_, Foo(_)) => 1

      // Every Bar precedes any Qux
      case (Bar(_), Qux(_)) => -1
      case (Qux(_), Bar(_)) => 1
    }
  }
}

Or (a little more clearly and concisely):

sealed trait Base
case class Foo(i: Int) extends Base
case class Bar(s: String) extends Base
case class Qux(f: Double) extends Base

object Base {
  implicit val orderingBase: Ordering[Base] = Ordering.fromLessThan {
    // First describe how to compare pairs of the same type
    case (Foo(i1), Foo(i2)) => i1 < i2
    case (Bar(s1), Bar(s2)) => s1 < s2
    case (Qux(f1), Qux(f2)) => f1 < f2

    // Every Foo precedes any non-Foo
    case (Foo(_), _) => true
    case (_, Foo(_)) => false

    // Every Bar precedes any Qux
    case (Bar(_), Qux(_)) => true
    case (Qux(_), Bar(_)) => false
  }
}

If your parent type is sealed, you'll get a nice error message if you miss a case.

Upvotes: 2

Related Questions