Далее я представлю только очень уменьшенные версии моего кода Scala. Достаточно, чтобы показать проблему. Ненужные блоки кода будут уменьшены до ...
.
Часть, которая работает
Я создал векторную библиотеку (то есть для моделирования математических векторов, а не векторов в смысле scala.collection.Vector
). Основная черта выглядит так:
trait Vec[C] extends Product {
def -(o:Vec[C]):Vec[C] = ...
...
}
Я создал множество подтипов для определенных векторов, например Vec2
для двумерных векторов или Vec2Int
, специализированный для двумерных Int
векторов.
Подтипы сужают типы возвращаемых данных некоторых операций. Например, вычитание Vec2Int
из другого вектора не вернет общий Vec[Int]
, а более конкретный Vec2Int
.
Кроме того, я объявил эти методы в очень специфических подтипах, таких как Vec2Int
, как final
, что позволило компилятору выбирать эти методы для встраивания.
Это работает очень хорошо, и я создал быструю и удобную библиотеку для векторных вычислений.
Основываясь на этом, я теперь хочу создать набор типов для моделирования основных геометрических фигур. Основная черта формы выглядит так:
trait Shape[C, V <: Vec[C]] extends (V=>Boolean) {
def boundingBox:Box[C,V]
}
Где Box
будет подтипом Shape
, моделирующим n-мерный прямоугольник.
Часть, которая не работает
Теперь я попытался определить поле:
trait Box[C, V <: Vec[C]] extends Shape[C,V] {
def lowCorner:V
def highCorner:V
def boundingBox = this
def diagonal:V = highCorner - lowCorner // does not compile
}
Метод diagonal
не компилируется, потому что метод Vec.-
возвращает Vec[C]
, а не V
.
Конечно, я мог бы заставить diagonal
вернуть Vec[C]
, но это было бы неприемлемо во многих отношениях. На этот раз я бы потерял оптимизацию компилятора для определенных Vec
подтипов. Кроме того, когда у вас, например, есть прямоугольник, описываемый двумя двумерными Float
векторами (Vec2Float
), имеет смысл предположить, что диагональ также равна Vec2Float
. Я не хочу потерять эту информацию.
Моя попытка решить проблему
Следуя примеру иерархии коллекций Scala, я ввел тип VecLike
:
trait VecLike[C, +This <: VecLike[C,This] with Vec[C]] {
def -(o:Vec[C]):This
...
}
и я заставил Vec
расширить его:
trait Vec[C] extends Product with VecLike[C, Vec[C]] ...
(Затем я хотел бы создать более конкретные подтипы VecLike
, например Vec2Like
или Vec3Like
, чтобы сопровождать мою иерархию Vec
типов.)
Теперь новое определение для Shape
и Box
выглядит следующим образом:
trait Shape[C, V <: VecLike[C,V] with Vec[C]] ...
trait Box[C, V <: VecLike[C,V] with Vec[C]] extends Shape[C,V] {
...
def diagonal:V = highCorner - lowCorner
}
Тем не менее, компилятор жалуется:
Error: type mismatch;
found: Vec[C]
required: V
Это смущает меня. Тип VecLike
явно возвращает This
в методе минус, что соответствует параметру типа V
типа Box
. Я вижу, что метод минус Vec
по-прежнему возвращает Vec[C]
, но почему компилятор не может использовать тип возврата метода минус VecLike
?
Как я могу исправить эту проблему?