Обоснование в функции линеаризации Scala


Насколько я понимаю, алгоритм функции линеаризации Scala является правильным-сначала поиск по глубине, прежде чем исключить все, кроме последнего вхождения каждого признака в результирующем списке.

Есть два аспекта этого алгоритма, которые кажутся мне довольно произвольными:

    Почему признаки исследуются в первую очередь, чем классы, а не наоборот ? Почему черты пересекаются справа налево, а не слева направо ?

Являются ли эти произвольные соглашения такими же хорошими, как есть еще кто-нибудь ? или, может быть, за этими проектными решениями стоит разумное объяснение ?

Обратите внимание, что мой вопрос касается только "горизонтального" упорядочения алгоритма.
1 3

1 ответ:

Являются ли эти произвольные соглашения такими же хорошими, как и любые другие?
Нет, они не произвольны. Линеаризация выполняется таким образом, потому что это единственный способ, который даст вам нормальный порядок разрешения метода (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
Мы ожидаем, что оба примера в приведенном выше примере будут иметь одинаковую линеаризацию. При использовании псевдонимов типов мы" складываем " дополнительные признаки на правой стороне, поэтому мы ожидаем, что признаки на правой стороне иметь наивысший приоритет в MRO, и поэтому прийти первым в линеаризации. Вот краткий пример, который я придумал, иллюстрирующий линеаризацию признаков и классов:
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-ins E и F.

Простая ссылка на этуГлаву 12 программирования в Scala, первое издание: черты . (Я не думаю, что это изменилось в последнее время, поэтому первое издание все еще должно быть точным.)