Scala compare of trait val member with abstract type
im trying to figure out how to express the below code using abstract types instead of using type parameters.
trait Key[T] extends Ordered[Key[T]] {
val key:T
}
case class DoubleKey(key:Double) extends Key[Double] {
def compare(that:Key[Double]):Int = this.key compare that.key
}
My current version looks as follows:
trait Key extends Order开发者_如何学Goed[Key] {
type K
val key:K
}
case class DoubleKey(val key:Double) extends Key {
type K = Double
def compare(that:Key):Int = this.key compare that.key.asInstanceOf[K]
}
But I'm not happy with the explicit casting to the type K: that.key.asInstanceOf[K]
. Are there better/other ways to achieve the ordering on an abstract member using abstract types?
I have also tried to ensure that the type of that:Key
is a Double
:
def compare(that:Key { type K = Double } ):Int = this.key compare that.key
but this also fails since the compiler doesnt think compare is defined. Also, is there a solution where compare
can be moved into the trait Key by restricting K (e.g. type K <: Ordered[K]
)?
I think you need at least one type parameter to indicate that DoubleKey
and say StringKey
are not comparable. It would not make sense to write DoubleKey(1.0) < StringKey("foo")
With your current design you could get a class cast exception:
case class StringKey(val key:String) extends Key {
type K = String
def compare(that:Key):Int = this.key compare that.key.asInstanceOf[K]
}
StringKey("b") < DoubleKey(1.0) // ClassCastException
Here is a definition that will enforce the constraint at compile time and also pull the definition of compare into the base member:
abstract class Key[K <% Ordered[K]] extends Ordered[Key[K]] {
val key:K
def compare(that:Key[K]): Int = key compare that.key
}
case class DoubleKey(key:Double) extends Key[Double]
case class StringKey(key:String) extends Key[String]
StringKey("b") < DoubleKey(1.0) // won't compile
Note that I used a view bound, as Double
or String
aren't subtypes of Ordered but there are implicit conversions available.
The only way to eliminate the type parameter from Key whilst still allowing Keys to be safely ordered is to define an implicit conversion from the Key type the the Ordered key type. This also makes it straightforward to factor out the compare method.
trait Key { type K ; val key : K }
object Key {
type PKey[KK] = Key { type K = KK }
implicit def keyIsOrdered[K <% Ordered[K]](lhs : PKey[K]) = new Ordered[PKey[K]] {
def compare(rhs : PKey[K]) = lhs.key compare rhs.key
}
}
case class DoubleKey(val key : Double) extends Key { type K = Double }
case class StringKey(val key : String) extends Key { type K = String }
Sample REPL session (nb. wrap trait/object Key in a dummy object to ensure the REPL sees them as companions),
scala> DoubleKey(23) < DoubleKey(34)
res3: Boolean = true
scala> StringKey("foo") < StringKey("bar")
res4: Boolean = false
scala> StringKey("foo") < DoubleKey(23)
<console>:14: error: could not find implicit value for parameter ord: scala.math.Ordering[ScalaObject]
StringKey("foo") < DoubleKey(23)
精彩评论