Search code examples
scalaimplicit-conversionimplicits

Is it possible to have implicit Ordering[Option[T] and Ordered[Option[T]] at the same time in Scala?


My code:

import Ordered.orderingToOrdered
import java.util.Date

val (d1, d2) = (Option(new Date()), Option(new Date()))
d1 compare d2

result with -Xlog-implicits:

Information:(268, 5) math.this.Ordering.Option is not a valid implicit value for    
     scala.math.Ordering[Option[java.util.Date]] because:
     diverging implicit expansion for type scala.math.Ordering[Option[java.util.Date]]
     starting with method ordered in trait LowPriorityOrderingImplicits
        d1 compare d2
        ^

Information:(268, 5) math.this.Ordering.comparatorToOrdering is not a valid implicit      
    value for scala.math.Ordering[Option[java.util.Date]] because:
    diverging implicit expansion for type scala.math.Ordering[Option[java.util.Date]]
    starting with method ordered in trait LowPriorityOrderingImplicits
        d1 compare d2
        ^

Error:(268, 5) diverging implicit expansion for type  
    scala.math.Ordering[Option[java.util.Date]]
    starting with method ordered in trait LowPriorityOrderingImplicits
    d1 compare d2
    ^

I think what happens here is a cycle between Ordered and Ordering - implicit Ordering[Option[Date]] is in scope because it's defined in object Ordering and it it is a subclass of java.util.Comparator, so you can use it to create another ordering...

This is compiled against scala 2.10, it seems that there were changes in this area in 2.11, but scalac entered infinite loop when I tried to upgrade scala.

Edit: Just to show I'm not clueless, I fixed it for the time being with substituting import Ordered._ with my import OrderedFix._:

/** Avoid implicit cycles between scala Ordered and Ordering */
trait OrderedFix[T] {
    def compare(that :T) :Int

    def < (that :T) = compare(that) <0
    def <=(that :T) = compare(that) <=0
    def > (that :T) = compare(that) >0
    def >=(that :T) = compare(that) >=0

}

object OrderedFix {
    implicit def ordered[T :Ordering](self :T) :OrderedFix[T] = new OrderedFix[T] {
        def compare(that :T) = implicitly[Ordering[T]].compare(self, that)
    }
}

But this is silly, that's what the stdlib should provide...


Solution

  • The use of the Ordered trait is considered bad style by many people. The preferred way is to stick to the Ordering type class. The following is an example of using just Ordering:

    val (d1, d2) = (Option(new Date()), Option(new Date()))
    val ord = Ordering[Option[Date]]
    import ord._
    
    compare(d1, d2)
    
    d1 < d2
    d1 equiv d2
    

    Here the import both gets you function compare into scope as well as extension methods that allow the infix operators shown in the last two lines.