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?
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.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With