开发者

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)
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜