逆变型的方法继承

时间:2015-07-27 23:31:42

标签: scala typeclass contravariance

我定义了两个类型类:

trait WeakOrder[-X] { self =>
  def cmp(x: X, y: X): Int
  def max[Y <: X](x: Y, y: Y): Y = if (cmp(x, y) >= 0) x else y
  def min[Y <: X](x: Y, y: Y): Y = if (cmp(x, y) <= 0) x else y
}

trait Lattice[X] { self =>
  def sup(x: X, y: X): X
  def inf(x: X, y: X): X
}

我想做以下事情:

trait TotalOrder[-X] extends Lattice[X] with WeakOrder[X] { self =>
  def sup(x: X, y: X): X = max(x, y)
  def inf(x: X, y: X): X = min(x, y)
}

但这是不可能的,因为逆变类型X出现在协变位置(supinf的返回值)。

但是,从语义上讲,这是正确的:maxmin类型为签名max[Y <: X](x: Y, y: Y): Y可以对max / min的返回值进行编码是两个论点之一。

我尝试执行以下操作:

trait TotalOrder[-X] extends Lattice[X] with WeakOrder[X] { self =>
  def sup[Y <: X](x: Y, y: Y): Y = max(x, y)
  def inf[Y <: X](x: Y, y: Y): Y = min(x, y)
}

但是,方法def sup[Y <: X](x: Y, y: Y): Y无法继承def sup[X](x: X, y: X): X。编译器抱怨类型签名不匹配。但是前者(具有现场方差注释)强加了比后者签名更强的类型限制。为什么前者不能继承后者呢?如何绕过TotalOrder[-X]的逆变类型限制(从语义上讲,总顺序是逆变的)?

1 个答案:

答案 0 :(得分:0)

这在语义上并不正确。应该从协变和逆变的定义中明确,但我试着举个例子:

假设我们有实体层次结构:

class Shape(s:Float)
class Circle(r:Float) extends Shape(Math.PI.toFloat * r * r)

让我们假设您可以创建逆变订单,就像您尝试的那样:

trait CircleOrder extends TotalOrder[Circle] {
   // compare by r
}

trait ShapeOrder extends TotalOrder[Shape] {
  // compare by s
}

根据对象的定义,Shape <: CircleCircleOrder <: ShapeOrder (CircleOrder是ShapeOrder的超类型)

假设我们有将CircleOrder作为参数的客户端 并用它来比较圈子:

def clientMethod(circleOrder:TotalOrder[Circle]) = {
  val maxCircle = circleOrder.max(???, ???) // expected to return Circle
  maxCircle.r // accessing field that is present only in circle
}

然后,根据继承的定义,应该可以通过 ShapeOrder而不是CircleOrder(请记住,ShapeOrder是子类型):

clientMethod(new ShapeOrder {/*...*/})

显然它不起作用,因为客户仍然希望命令返回Circles,而不是Shapes。

我认为在您的情况下,最合理的方法将使用常规泛型。

更新

这是你如何确保类型安全,但它有点难看。

    trait WeakOrder[-X] {
      def cmp(x: X, y: X): Int
      def max[T](x: X with T, y: X with T): T =
        if (cmp(x, y) >= 0) x else y
      def min[T](x: X with T, y: X with T): T =
        if (cmp(x, y) <= 0) x else y
    }

    trait Lattice[X] {
      def sup[T](x: X with T, y: X with T): T
      def inf[T](x: X with T, y: X with T): T
    }

    trait TotalOrder[-X] extends Lattice[X] with WeakOrder[X] {
      def sup[T](x: X with T, y: X with T): T = max(x, y)
      def inf[T](x: X with T, y: X with T): T = min(x, y)
    }