Шаблон торта с переопределением абстрактного типа не работает с верхними границами типов
Я хочу переопределить абстрактный тип в черту с <:
а не с =
(например, ответ здесь Scala Upper Bounds: значение не является членом параметра типа).
Я хочу использовать шаблон торта, но это не работает, я не понимаю, почему?
trait A {
def ping = println("ping")
}
trait Cake {
type T
}
trait S { this: Cake =>
type T = A
def t: T
t.ping
}
ОК, этот пример запускается, но в моем реальном случае я хочу переопределить тип с помощью <:
а не с =
. Кажется, невозможно получить доступ к функции t, почему?
trait S { this: Cake =>
type T <: A
def t: T
t.ping
}
вернуть ошибку value ping is not a member of S.this.T
1 ответ
Это недостаток системы типов Scala. При определении членов в миксине Scala использует два правила: во-первых, конкретное всегда переопределяет абстрактное. Во-вторых, если два члена являются либо конкретными, либо обоими абстрактными, то побеждает тот, который придет позже в порядке линеаризации.
Кроме того, собственный тип черты
trait S { this: C => ... }
неявно увеличивается до
trait S { this: S with C => ... }
так что определения в признаке S могут быть доступны в пределах S. В вашем случае признак S рассматривается как:
trait S { this: S with Cake =>
type T = A
def t: T
t.ping
}
Теперь, пока T конкретен, это нормально, потому что он переопределяет абстрактный T в Cake. Но если T является абстрактным, тот, что в Cake, появляется позже в порядке линеаризации и побеждает. И этот T не имеет верхней границы, поэтому член не пингуется. Один из способов исправить это - изменить порядок линеаризации, написав:
trait S { this: Cake with S =>
type T <: A
def t: T
t.ping
}
Было бы лучше, если бы в Scala было другое правило, которое гласит, что все ограничения членов абстрактного типа объединяются в миксине, вместо того, чтобы выбирать один элемент в соответствии с порядком линеаризации. Это изменение, которое мы хотим рассмотреть в будущем, но мы должны быть осторожны с обратной совместимостью.