Scala: незаконное наследование; сам тип Y не соответствует X selftype SELF

У меня есть черта, которая принимает параметр типа, и я хочу сказать, что объекты, реализующие этот признак, также будут соответствовать этому параметру типа (используя generics для совместимости с Java)

Следующий код:

trait HandleOwner[SELF <: HandleOwner[SELF]] {
self : SELF =>
    // ...
    def handle: Handle[SELF]
}

trait Common[SELF <: Common[SELF]]  extends HandleOwner[SELF] {
    // ...
}

Дает мне следующую ошибку:

illegal inheritance;  self-type test.Common[SELF] does not conform to
test.HandleOwner[SELF] selftype SELF

Если я изменил Common на:

trait Common[SELF <: Common[SELF]]  extends HandleOwner[SELF] {
self : SELF =>
    // ...
}

Затем ошибка исчезает.

Почему я должен повторять одно и то же объявление в каждом небетоновом типе. Если бы у меня был базовый класс и я говорю "extends Comparable", мне не нужно повторять "extends Comparable" в каждом производном типе, если конкретные классы реализуют метод compareTo. Я думаю, что здесь должно быть одно и то же. Я просто говорю, что тип, расширяющий HandleOwner, также будет SELF, и компилятор должен просто принять его и принять его во внимание, не требуя, чтобы каждый небетонный подтип повторял то же самое снова.

Я делаю это, чтобы избежать использования класса, но я буду буквально расширять каждый класс из этой черты, и я не вижу, что мне придется повторять эти объявления сотни или даже тысячи раз!

Ответы

Ответ 1

Тип "Я" больше похож на общее ограничение, чем наследование. При class C[A <: B] ограничение должно повторяться все время в подклассах: class D[A <: B] extends C[A]. Ограничение должно повторяться до тех пор, пока оно не будет выполнено, то есть пока вы не выбрали фактический тип параметра, который действительно удовлетворяет <: B. То же самое для типа self. Написание self: A => не приводит к расширению вашего типа A. Это гарантирует, что в конечном итоге его нужно будет смешивать с A до того, как он будет фактически создан.

Напротив, когда вы расширяете Comparable, вы сделали свой класс a Comparable, а не ограничиваете его позже. Но тот факт, что вам нужно реализовать compareTo, все равно нужно повторять вместе с abstract, пока вы его не реализуете.

Конечно, компилятор мог обойтись без повторения <: B, self: A => и abstract, информация доступна для него. Это выбор языка. По крайней мере, повторение self: A => не отличается от правил везде.