Далее я представлю только очень сокращенные версии моего кода 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-мерный блок.
Теперь я попытался определить box :
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
}
Метод диагональ
не компилируется, потому что метод Vec .-
возвращает Vec [C]
, а не V
.
Конечно, я мог бы заставить диагональ
возвращать 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
?
Как я могу исправить эту проблему?