Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to provide implicit conversion from DateTime to Ordered using implicit conversion to Comparable

Tags:

scala

I'm trying to use >=,>, etc. with DateTime (joda), and the only way I could get it to work was using this implicit conversion

implicit def dateTime2ordered(x: DateTime): Ordered[DateTime] =
new Ordered[DateTime] with Proxy {
  val self = x

  def compare(y: DateTime): Int = {
    x.compareTo(y)
  }
}

I would have preferred a more generic form such as

implicit def comparable2ordered[A <: Comparable[A]](x: A): Ordered[A] =
    new Ordered[A] with Proxy {
      val self = x

      def compare(y: A): Int = {
        x.compareTo(y)
      }
    } 

But the compiler can't find this conversion, and after trying to invoke it directly, I've gotten the following message claiming that DateTime is not of type Comparable[A]. After checking the source for DateTime, I saw that it only implements Comparable as a raw type.

I was able to get it work using

 implicit def comparable2ordered[A <: Comparable[_]](x: A): Ordered[A] =
    new Ordered[A] with Proxy {
      val self = x

      def compare(y: A): Int = {
        x.compareTo(y)
      }
    } 

My question is: Is this the correct Scala treatment of this problem, or would the wildcard type bound cause future problems with type checking?

like image 980
user44242 Avatar asked Jan 13 '11 07:01

user44242


3 Answers

I stumbled across this question because I, too, was looking to compare joda DateTime objects using relational operators.

Daniel's answer pointed me in the right direction: the implicits present in scala.math.Ordered will convert an instance of A extends java.lang.Comparable[A] to an Ordered[A] - they just need to be brought into scope. The easiest way to do this (which I learned here, btw) is with the implicitly method:

val aOrdering = implicitly[Ordering[A]]
import aOrdering._

The catch is that org.joda.time.DateTime doesn't extend or implement Comparable itself, it inherits (indirectly) from org.joda.time.ReadableInstant, which does extends Comparable. So this:

val dateTimeOrdering = implicitly[Ordering[DateTime]]
import dateTimeOrdering._

won't compile, because the DateTime doesn't extend Comparable[DateTime]. To use Ordered's relational operators on a DateTime, you have to do this instead:

val instantOrdering = implicitly[Ordering[ReadableInstant]]
import instantOrdering._

which works because ReadableInstant extends Comparable[ReadableInstant], and the implicit conversions in Ordered can convert it to an Ordered[ReadableInstant].

So far, so good. However, there are situations where an Ordered[ReadableInstant] isn't good enough. (The one I encountered is with ScalaTest's greater and less than Matchers.) To get an Ordered[DateTime], I was forced to do this:

implicit object DateTimeOrdering extends Ordering[DateTime] {
  def compare(d1: DateTime, d2: DateTime) = d1.compareTo(d2)
}

It seems like there ought to be an easier way, but I couldn't figure one out.

like image 115
Mark Tye Avatar answered Nov 11 '22 01:11

Mark Tye


Is fine, a raw type "Comparable" gets translated to "Comparable[_]" in Scala.

They're called Existential Types, Comparable[_] is a shorthand for " Comparable[T] forSome { type T } " (since version 2.7 see http://www.scala-lang.org/node/43 )

See also "Existential types" in http://www.artima.com/scalazine/articles/scalas_type_system.html

like image 26
GClaramunt Avatar answered Nov 11 '22 02:11

GClaramunt


See, the thing is, this already exists. Well, kind of... If you look inside object Ordered, where implicit conversions are looked for, you'll find this:

implicit def orderingToOrdered [T] (x: T)(implicit ord: Ordering[T]) : Ordered[T]

So, as long as there's an Ordering[T] available, one can produce an Ordered[T]. Now, to look for an Ordering[T] inside the Ordering object:

implicit def ordered [A] (implicit arg0: (A) ⇒ Comparable[A]) : Ordering[A]

So, if you pass a comparable: A with Comparable[A] to something expecting an Ordered[A], it will do this:

Ordered.orderingToOrdered(comparable)(Ordering.ordered(Predef.identity(comparable)))

Now, as to your question: using existential types is the correct way of handling Java raw types. It is theoretically possible for this to result in an incorrect ordering, but, in practice, extremely unlikely. You may have problems with implicit ambiguity, though, since Scala already have a Comparable => Ordered implicit conversion, as seen above.

like image 20
Daniel C. Sobral Avatar answered Nov 11 '22 01:11

Daniel C. Sobral