Using pattern matching for Ordered.compare in Scala

pattern-matching, scala

Solution

The preferred way in Scala is to provide an implicit Ordering instead of Ordered, which is much more flexible and doesn't give a headache concerning inheritance.

Concerning the pattern matching I see no better way, because the result of the compare methods are `Int`s, which are not guaranteed to be -1, 0, 1. Haskell's solution to give back "enum" objects (LT, EQ, GT) is much cleaner and pattern matchable, but it seems Scala followed the C++/Java tradition here for compatibility reasons.

Of course you could roll out your own comparision "framework":

abstract sealed class CompResult(val toInt:Int) {
  def andThen(next: => CompResult): CompResult
}
case object LT extends CompResult(-1) {
 def andThen(next: => CompResult) = LT
}
case object EQ extends CompResult(0) {
  def andThen(next: => CompResult) = next
}
case object GT extends CompResult(1) {
  def andThen(next: => CompResult) = GT
}

implicit def int2Comp(n:Int) =
   if (n == 0) EQ else if (n < 0) LT else GT


(("sdkfhs" compareTo "fldgkjdfl"):CompResult) match {
  case LT => println("less")
  case EQ => println("same")
  case GT => println("more")
}

In your case you could write:

case class Person(firstName: String, lastName: String, age: Int)
  extends Ordered[Person] {
  def compare(that: Person): Int = {
    (this.lastName compareTo that.lastName).
    andThen (this.firstName compareTo that.firstName).
    andThen (this.age compare that.age).toInt
  }
}

Problem

I have the following Scala class: ``` case class Person(firstName: String, lastName: String, age: Int) extends Ordered[Person] { def compare(that: Person): Int = { if (this.lastName < that.lastName) -1 else if (this.lastName > that.lastName) 1 else if (this.firstName < that.firstName) -1 else if (this.firstName > that.firstName) 1 else this.age compare that.age } } ``` to allow sorting by lastName, firstName, and age. How can I write this using pattern matching? I've come up with the following, but is there a better way? ``` case class Person(firstName: String, lastName: String, age: Int) extends Ordered[Person] { def compare(that: Person): Int = { that match { case Person(_, thatLastName, _) if this.lastName < thatFile => -1 case Person(_, thatLastName, _) if this.lastName > thatFile => 1 case Person(thatFirstName, _, _) if this.firstName < thatFirstName => -1 case Person(thatFirstName, _, _) if this.firstName > thatFirstName => 1 case Person(_, _, thatAge) => this.age compare thatAge } } } ``` UPDATE: Changed to use `Ordering[A]` as per Landei's answer: ``` implicit val personOrdering = new Ordering[Person] { def compare(first: Person, second:Person): Int = { second match { case Person(_, thatLastName, _) if first.lastName < thatLastName => -1 case Person(_, thatLastName, _) if first.lastName > thatLastName => 1 case Person(thatFirstName, _, _) if first.firstName < thatFirstName => -1 case Person(thatFirstName, _, _) if first.firstName > thatFirstName => 1 case Person(_, _, thatAge) => first.age compare thatAge } } } case class Person(firstName: String, lastName: String, age: Int) ``` but it seems awkward that I only match `second`. How can I make it more "elegant"?

Original source