14

Is there a way for me to define the same implicit ordering for two different classes?

I tried to do something along the following lines but it doesn't detect the ordering.

abstract class Common
case class A extends Common
case class B extends Common

implicit val KeyOrdering = new Ordering[Common] {
    override def compare(x: Common, y: Common): Int = {
      x.toString.compareTo(y.toString)
   }
}
Olshansky
  • 5,904
  • 8
  • 32
  • 47
  • 1
    `extends` is a reserved keyword and not a type. Do you mean `Common`? – Kigyo Sep 08 '14 at 22:28
  • Yup, that's what I meant. Thanks for pointing that out. – Olshansky Sep 08 '14 at 22:30
  • Yea, I'm also not sure why this won't work. `List(A(), B()).sorted` says that no implicit ordering can be found, yet explicitly `List(A(), B()).sorted(KeyOrdering)` works. – Kigyo Sep 08 '14 at 22:34

2 Answers2

29

As noted by @ntn, the inferred type of your list - the least upper bound of its two elements - is Product with Serializable with Common. As scala.Ordering is not contravariant on its type parameter, implicit resolution fails because it does not hold that Ordering[Common] <: Ordering[Product with Serializable with Common].

You can work around this by writing the implicit ordering so that it always has the exact type of the implicit parameter under consideration:

abstract class Common
case class A() extends Common
case class B() extends Common

object Common {
  implicit def ordering[A <: Common]: Ordering[A] = new Ordering[A] {
    override def compare(x: A, y: A): Int = {
      x.toString.compareTo(y.toString)
    }
  }
}

Or for concision:

object Common {
  implicit def ordering[A <: Common]: Ordering[A] = Ordering.by(_.toString)
}
matanster
  • 15,072
  • 19
  • 88
  • 167
J Cracknell
  • 3,498
  • 1
  • 19
  • 13
  • 3
    I see by your other answer that you are an expert. http://stackoverflow.com/questions/19345030/easy-idiomatic-way-to-define-ordering-for-a-simple-case-class – som-snytt Sep 09 '14 at 00:57
  • Speaking of which, I have updated that answer with a note about this specific issue. – J Cracknell Sep 09 '14 at 05:21
  • The `Ordering` trait has a single abstract method to implement: the `compare` function. This is known as a Single Abstract Method (SAM) type. Since Scala 2.12 and the introduction of lambda syntax for SAM types, the implementation in the first example can be elegantly simplified to: `implicit def ordering[A <: Common]: Ordering[A] = (x: A, y: A) => x.toString.compareTo(y.toString)` – François Jun 30 '23 at 11:48
2

If you remove the case class for A and B (or even only for one of them), then it works. For List(A(), B()).sorted, it fails to find an Ordering for Product with Serializable with C, as the base class for A and B is Product with C (due to A and B being both case classes).

If you are creating a list with elements of two different base types, I assume you want a list of type List[C], in which you can declare the elements before using them (or get them from some function which returns type C.

val a: C = A()
val b: C = B()
List(a,b).sorted
ntn
  • 1,167
  • 1
  • 8
  • 11