Обоснование в функции линеаризации Scala
Насколько я понимаю, алгоритм функции линеаризации Scala является правильным-сначала поиск по глубине, прежде чем исключить все, кроме последнего вхождения каждого признака в результирующем списке.
Есть два аспекта этого алгоритма, которые кажутся мне довольно произвольными:
- Почему признаки исследуются в первую очередь, чем классы, а не наоборот ?
Почему черты пересекаются справа налево, а не слева направо ?
Являются ли эти произвольные соглашения такими же хорошими, как есть еще кто-нибудь ? или, может быть, за этими проектными решениями стоит разумное объяснение ?
Обратите внимание, что мой вопрос касается только "горизонтального" упорядочения алгоритма.1 ответ:
Являются ли эти произвольные соглашения такими же хорошими, как и любые другие?Нет, они не произвольны. Линеаризация выполняется таким образом, потому что это единственный способ, который даст вам нормальный порядок разрешения метода (MRO).Почему признаки исследуются в первую очередь, чем классы, а не наоборот ?Это справедливо только для данного определения класса, поскольку признаки, которые оно расширяет, предшествуют классу, который оно расширяет. Это следует из тот факт, что суперклассы/черты изучаются справа налево, подводит нас ко второму пункту.Почему черты пересекаются справа налево, а не слева направо ?Вот очень простой мотивирующий пример для этого.Мы ожидаем, что оба примера в приведенном выше примере будут иметь одинаковую линеаризацию. При использовании псевдонимов типов мы" складываем " дополнительные признаки на правой стороне, поэтому мы ожидаем, что признаки на правой стороне иметь наивысший приоритет в MRO, и поэтому прийти первым в линеаризации. Вот краткий пример, который я придумал, иллюстрирующий линеаризацию признаков и классов:type MyType = T1 with T2 // Creating new instance using alias new MyType with T3 // Creating new instance directly new T1 with T2 with T3
class Base { override def toString = "Base" } trait A { abstract override def toString = "A " + super.toString } trait B { abstract override def toString = "B " + super.toString } trait C { abstract override def toString = "C " + super.toString } trait D { abstract override def toString = "D " + super.toString } trait E { abstract override def toString = "E " + super.toString } trait F { abstract override def toString = "F " + super.toString } class X extends Base with A with B { override def toString = "X " + super.toString } class Y extends X with C with D { override def toString = "Y " + super.toString } class Z extends Y with E with F { override def toString = "Z " + super.toString } new Z // res0: Z = Z F E Y D C X B A Base
Вы можете видеть из вывода
toString
, что линеаризация здесьZ F E Y D C X B A Base
, что именно то, что я ожидал бы с данной иерархией. Например, посколькуZ
расширяетY
, который смешивается-в чертеC
, мы ожидали бы, что поведениеY
придет доC
, но послеZ
и его mix-insE
иF
.Простая ссылка на этуГлаву 12 программирования в Scala, первое издание: черты . (Я не думаю, что это изменилось в последнее время, поэтому первое издание все еще должно быть точным.)