Как Traits в Scala позволяют избежать «ошибки алмаза»?

16

(Примечание: я использовал «error» вместо «problem» в названии по понятным причинам ..;)).

Я немного изучил черты характера в Scala. Они похожи на интерфейсы в Java или C #, но допускают реализацию метода по умолчанию.

Мне было интересно: не может ли это привести к «проблеме алмазов», поэтому многие языки избегают множественного наследования?

Если да, то как Scala справляется с этим?

Авив Кон
источник
Поделиться своими исследованиями помогает всем . Расскажите нам, что вы пробовали и почему это не соответствует вашим потребностям. Это свидетельствует о том, что вы потратили время, чтобы попытаться помочь себе, избавляет нас от повторения очевидных ответов и, прежде всего, помогает получить более конкретный и актуальный ответ. Также смотрите Как спросить
gnat
2
@gnat: это концептуальный вопрос, а не конкретный проблемный вопрос. Если он спрашивал: «У меня есть этот класс в Scala, и он вызывает у меня проблемы, которые, я думаю, могут быть связаны с Алмазной проблемой, как я могу это исправить?» тогда ваш комментарий будет уместным, но тогда вопрос будет относиться к SO. : P
Мейсон Уилер
@MasonWheeler Я тоже немного читал о Scala. И первый поиск слова «алмаз» в том, что я прочитал, дал мне ответ: «У черты есть все особенности конструкции интерфейса Java. Но у черт могут быть реализованы методы на них. Если вы знакомы с Ruby, черты похожи к миксинам Руби. Вы можете смешивать много признаков в одном классе. Характеристики не могут принимать параметры конструктора, кроме того, что они ведут себя как классы. Это дает вам возможность иметь что-то, что приближается к множественному наследованию без проблемы с алмазом ». Недостаток усилий в этом вопросе кажется довольно вопиющим
комар
7
Чтение этого утверждения не говорит вам, КАК это так.
Майкл Браун

Ответы:

22

Проблема алмазов заключается в невозможности решить, какую реализацию метода выбрать. Scala решает эту проблему, определяя, какую реализацию выбрать в рамках языковых спецификаций (см. Часть о Scala в этой статье Википедии ).

Конечно, то же самое определение порядка может также использоваться в множественном наследовании класса, так зачем беспокоиться о чертах?

Причиной ИМО являются конструкторы. Конструкторы имеют несколько ограничений, которых нет у обычных методов - их можно вызывать только один раз для каждого объекта, их нужно вызывать для каждого нового объекта, а конструктор дочернего класса должен вызывать конструктор своего родителя в качестве первой инструкции (большинство языков сделайте это неявно для вас, если вам не нужно передавать параметры).

Если B и C наследуют A и D наследуют B и C, и оба конструктора B и C вызывают конструктор A, тогда конструктор D дважды вызовет конструктор A. Определение того, какую реализацию выбрать, как это делал Scala с методами, здесь не сработает, потому что должны вызываться как конструкторы B, так и C.

Черты избегают этой проблемы, так как у них нет конструкторов.

Идан Арье
источник
1
Можно использовать линеаризацию C3 для вызова конструкторов один раз и только один раз - именно так Python выполняет множественное наследование. Вдобавок ко всему, линеаризация для D <B | C <алмаз - это D -> B -> C -> A. Более того, поиск в Google показал, что черты Scala могут иметь изменяемые переменные, так что, безусловно, есть конструктор где-то там? Но если он использует композицию под капотом (я не знаю, никогда не использовал Scala), нетрудно увидеть, что B и C могут делиться в случае A ...
Doval,
... Черты кажутся очень кратким способом выразить весь шаблон, который объединяет наследование интерфейса и состав + делегирование, что является правильным способом повторного использования поведения.
Довал
@Doval Мой опыт использования конструкторов в Python с множественным наследованием заключается в том, что они - королевская боль. Каждый конструктор не может знать, в каком порядке он будет вызываться, поэтому он не знает, какова подпись его родительского конструктора. Обычное решение для каждого конструктора - взять кучу аргументов с ключевыми словами и передать неиспользуемые аргументы в свой супер-конструктор, но если вам нужно работать с существующим классом, который не следует этому соглашению, вы не можете безопасно наследовать от Это.
James_pic
Другой вопрос заключается в том, почему C ++ не выбрал разумную политику для алмазной проблемы?
пользователь
20

Scala избегает алмазной проблемы с помощью так называемой «черты линеаризации». По сути, он ищет реализацию метода по признакам, которые вы расширяете справа налево. Простой пример:

trait Base {
   def op: String
}

trait Foo extends Base {
   override def op = "foo"
}

trait Bar extends Base {
   override def op = "bar"
}

class A extends Foo with Bar
class B extends Bar with Foo

(new A).op
// res0: String = bar

(new B).op
// res1: String = foo

Тем не менее, список черт, которые он ищет, может содержать больше, чем те, которые вы явно указали, поскольку они могут расширять другие черты. Подробное объяснение дано здесь: черты как составные модификации и более полный пример линеаризации здесь: почему не множественное наследование?

Я полагаю, что в других языках программирования такое поведение иногда называют «порядком разрешения методов» или «MRO».

lutzh
источник