Что делает это требование класса циклическим определением класса?
Проверщик типов считает это требование класса интерфейсом IBase
быть циклическим:
<?hh // strict
interface IBase {
require extends Derived;
}
class Derived implements IBase {}
// Cyclic class definition : IBase Derived (Typing[4013])
Насколько я понимаю, ограничение просто мешает всем потомкам implements IBase
без extends Derived
, Есть ли дыра с этим, которую я не вижу?
Почему меня это волнует?
Мне интересен интерфейс, который хочет сравнивать с другими экземплярами самого себя или его подтипов.
<?hh // strict
interface Comparable<-T as Comparable<T>> {
require extends ArtificialCeiling;
public function compare(T $comparee): bool;
}
abstract class ArtificialCeiling implements Comparable<ArtificialCeiling> {
abstract public function compare(ArtificialCeiling $comparee): bool;
}
(this
здесь нет ответа, потому что this
не звучит в контравариантных позициях, особенно в интерфейсах)
Предположим, теперь мы хотим принять и сохранить упаковку Comparable
но нас не волнует, какой тип Comparable
это тащится вокруг. Обычно мы просто параметризуем с верхней границей, или mixed
если его без ограничений.
Проблема в том, что верхняя граница для Comparable
является Comparable<Comparable<Comparable<...
навсегда, но у меня нет выносливости, чтобы напечатать это на всю вечность. Без таких экзистенциальных типов, как Scala или нескольких ограничений, таких как TComparable as Comparable & ArtificialCeiling
Мы должны прибегнуть к чему-то менее очевидному. require extends ArtificialCeiling
было бы похоже на множественное ограничение, и без этой загадочной циклической проблемы это было бы аккуратным решением.
Другая естественная альтернатива для принимающего класса - добавить параметр в свой список параметров как TComparable as Comparable<TComparable>
, но это побеждает принцип не заботиться о TComparable
,
1 ответ
Ну, я не эксперт, но сообщение мне кажется ясным: определение является циклическим, потому что Derived
использования IBase
а также IBase
Рекомендации Derived
, Согласно документации:
Требуется расширяет, следует понимать буквально. Класс должен расширять необходимый класс; таким образом, фактический требуемый класс не соответствует этому требованию. Это позволяет избежать некоторых тонких циклических зависимостей при проверке требований.
Я думаю, что для этого нужно указать требование к классу-предку и реализовать интерфейс в неабстрактном производном классе (ах). Или, может быть, просто реализовать compare
как обычный метод в классе предков, вместо использования черты.