Расширить класс Scala, который расширяет порядок - PullRequest
3 голосов
/ 30 ноября 2009

У меня проблемы с расширением базового класса, расширяющего Ordered [Base]. Мой производный класс не может расширять Ordered [Derived], поэтому его нельзя использовать в качестве ключа в TreeMap. Если я создаю TreeMap [Base], а затем просто переопределяю сравнение в Derived, это работает, но это не то, что я хочу. Я хотел бы иметь возможность иметь производный класс в качестве ключа. Есть ли способ обойти это?

case class A(x: Int) extends Ordered[A] {
  def compare(that: A) = x.compare(that.x)
}

// Won't compile
//  case class B(val y : Int) extends A(1) with Ordered[B] {
//    def compare(that: B) = x.compare(that.x) match {
//      case 0 => y.compare(that.y)
//      case res => res
//    }
//  }

// Compiles but can't be used to define a TreeMap key
case class B(y: Int) extends A(1) {
  override def compare(that: A) = that match {
    case b: B => x.compare(b.x) match {
      case 0 => y.compare(b.y)
      case res => res
    }
    case _: A => super.compare(that)
  }
}

def main(args: Array[String]) {
  TreeMap[B, Int]() // Won't compile
}

Редактировать

Это обсуждение в списке рассылки scala кажется очень актуальным, но оно меня немного теряет.

Ответы [ 3 ]

4 голосов
/ 30 ноября 2009

Вы можете использовать преобразование типов из B в Ordered [B]:

class OrderedB(me : B) extends Ordered[B]{
    def compare(that: B) = me compare that
}
collection.immutable.TreeMap.empty[B, Int](new OrderedB(_))

Я думаю, что B всегда должен быть подтипом A, что подразумевает порядок [A], тип A которого инвариантен. Он не может определить второй метод сравнения для реализации Order [B] с тем же типом ошибки, что и метод сравнения из Ordered [A].

В качестве альтернативы вы можете определить версии неявного типа от B до Ordered [B]:

implicit def orderedA2orderedB[B <: A with Ordered[A]](b : B) : Ordered[B] = b.asInstanceOf[Ordered[B]]
collection.immutable.TreeMap[B, Int]()

Это должно быть действительно. Я не знаю, как это выразить в системе типов без приведения.

3 голосов
/ 30 ноября 2009

Черта Ordered принимает параметр. Параметр типа предоставлен, но он работает так же, как и любой другой параметр. Когда вы расширяете его дважды, в базовом классе и в подклассе, вы не «импортируете» две версии Ordered. Вместо этого происходит линеаризация классов, и вы импортируете ее только один раз. По этой причине вы не можете передать ему два разных параметра.

Теперь есть причина, по которой TreeMap не требует subclass из Ordered, просто преобразование из вашего класса в Ordered. Именно для того, чтобы сделать такие вещи возможными. Вместо того, чтобы расширять эти вещи напрямую, вы должны указывать на них:

scala> class A(val x: Int)
defined class A

scala> class B(x : Int, val y : Int) extends A(x)
defined class B

scala> import scala.collection.immutable.TreeMap
import scala.collection.immutable.TreeMap

scala> class AOrd(a: A) extends Ordered[A] {
     |   def compare(that: A) = a.x.compare(that.x)
     | }
defined class AOrd

scala> object AOrd {
     | implicit def toAOrd(a: A) = new AOrd(a)
     | }
defined module AOrd

scala> class BOrd(b: B) extends Ordered[B] {
     |   def compare(that: B) = b.x.compare(that.x) match {
     |     case 0 => b.y.compare(that.y)
     |     case res => res
     |   }
     | }
defined class BOrd

scala> object BOrd {
     | implicit def toBOrd(b: B) = new BOrd(b)
     | }
defined module BOrd

scala> import AOrd._
import AOrd._

scala> import BOrd._
import BOrd._

scala> TreeMap[B, Int]()
res1: scala.collection.immutable.SortedMap[B,Int] = Map()
2 голосов
/ 30 ноября 2009

Вы можете поместить неявный Ordering [B] в область видимости где-то, например так:

  object BOrdering extends Ordering[B] {
    def compare(a: B, b: B) = a.compare(b)
  }
  implicit val bo = BOrdering
  TreeMap[B, Int]() // Now it works!

РЕДАКТИРОВАТЬ : Это только в Scala 2.8 (спасибо, Кен)

...