简体   繁体   中英

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 Ordered[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 (eg 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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM