Почему трейты Scala на самом деле не трейты?

Кто-то недавно сказал мне, что трейты Scala не являются "настоящими" трейтами, а на самом деле это просто примеси. К сожалению, у меня не было возможности спросить его, почему. Кто-нибудь знает, что он имел в виду?

Правка: В качестве определения «признаков» я ссылался на диссертацию Натанаэля Шерли. и концептуальный документ, представляющий особенности. Одна ключевая функция, которая отсутствует в большинстве миксинов и/или реализаций множественного наследования, — это возможность переименовывать методы при их импорте, чтобы избежать коллизий/неоднозначностей. Может ли Scala сделать это?


person Neil Traft    schedule 09.03.2011    source источник
comment
Придется определить черту против миксина. Например, примеси Ruby не похожи на трейты Scala в реализации (я думаю, что миксины Ruby могут быть ближе к упомянутым истинным трейтам? По сути, это заглушка в MRO ;-), что, вероятно, отбросило бы аргумент — термины нуждаются в лучшем определении: -)   -  person    schedule 09.03.2011
comment
Если мы обнаружим, что это не настоящие черты, то лучше кому-нибудь пойти и обновить Википедию. en.wikipedia.org/wiki/Trait_(computer_science)   -  person Synesso    schedule 09.03.2011
comment
Спасибо за подсказку, PST, я отредактировал свой пост, чтобы уточнить.   -  person Neil Traft    schedule 09.03.2011
comment
Чтобы узнать о реальной реализации признаков, взгляните на в Perl. Moose::Roles или traits.js Javascript. Кроме того, я предложил аналогичную функцию для C# ( что может быть очень похоже на Java). Я также начал реализовать это видение.   -  person Jordão    schedule 04.06.2011
comment
в диссертации Шарли упоминаются и обсуждаются особенности Scala (стр. 123) как «особо интересная» адаптация. Основные отличия заключаются в следующем: - трейты Scala моделируются как абстрактные классы, которые не инкапсулируют состояние, - трейты Scala не только могут быть составлены, но также могут быть унаследованы - трейты Scala поддерживают дженерики - трейты Scala не поддерживают псевдонимы и исключения   -  person Edoardo Vacchi    schedule 12.05.2014


Ответы (3)


Одно ключевое различие между миксинами и трейтами заключается в том, что у миксинов есть поля, а у трейтов — нет. Перефразируя оригинальную статью, черта:

  • предоставляет методы, реализующие поведение
  • требуются методы, которые параметризируют предоставленное поведение
  • не указывать и не обращаться к каким-либо полям
  • симметрично составлены
  • могут быть вложены, что эквивалентно сглаженным признакам

На первый взгляд кажется, что третий пункт не работает в реализации на Scala. Однако трейты могут обращаться только к общедоступным полям, которые защищены неявными геттерами и сеттерами. Далее в документе описывается, что это приемлемо для реализации трейтов.

Вы указываете, что ключевой особенностью трейтов является то, что методы могут быть переименованы при их импорте. Это невозможно, учитывая ограничения JVM. Последовательное обсуждение этого можно найти здесь: http://scala-programming-language.1934581.n4.nabble.com/Trait-method-aliasing-td2322026.html, особенно сообщения Дэвида Поллака.

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

  • Держите их маленькими, с целью их повторного использования.
  • Укажите поведение, а не состояние.
person Steve McDowell    schedule 26.04.2012

Я думаю, что это, вероятно, связано с тем, что есть в Scala, а не с тем, что было предложено в исходной документе. .

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

Порядок разрешения методов в Scala — странный зверь, у него есть собственный алгоритм диспетчеризации методов. Это не C3 Дилана, который используется в Python с некоторыми заметными проблемами, но имеет все проблемы, которые с ним связаны. Хуже того, я могу найти MRO объекта Python, вызвав его метод .mro(). В Scala нет эквивалента.

Я могу сказать вам, что мне не очень нравится запускать в голове алгоритм Scala MRO каждый раз, когда мне нужно искать, где будет разрешен метод.

person Y.H Wong    schedule 09.03.2011
comment
Что касается вашего последнего пункта: хорошая среда IDE может помочь вам в этом, например, в IDEA, удерживая нажатой клавишу Ctrl, и щелкните вызов метода, чтобы перейти к определению метода. - person Jesper; 09.03.2011
comment
Линеаризация Scala четко определена, детерминирована и не так уж сложна (позже побеждает черта). Самое главное, что его можно разрешить статически, то есть компилятор может привязать используемую реализацию к одному классу. Я не уверен, является ли исключение методов хорошей функцией, поскольку с ее помощью вы можете нарушить принцип подстановки; Я склонен утверждать, что если вам это нужно, ваш дизайн плох. В любом случае вам следует избегать конфликтующих методов. - person Raphael; 09.03.2011
comment
@ Рафаэль, я согласен с тобой. Нарушение принципа подстановки, вероятно, плохая идея для языка, который не использует утиную печать. По большей части множественное наследование в Scala работает нормально, если у вас нет слишком сложной иерархии, но когда она есть, простое размышление о том, куда будут разрешаться методы, вероятно, вызовет серьезные головные боли. Тем больше причин не поощрять плохой дизайн. На самом деле, кажется, нет лучшего способа сделать это в Scala. - person Y.H Wong; 09.03.2011
comment
@Wong: Спасибо, я думаю, что линеаризация - это именно то, к чему я стремился. Это противоречит документам, которые я читаю о трейтах, в которых, кажется, говорится, что полная реализация трейтов требует некоторого синтаксиса, позволяющего программисту разрешать такие конфликты, а не подчиняться какому-то произвольному алгоритму разрешения во время выполнения. В конце концов, у меня нет полного контроля над тем, какой метод на самом деле вызывается. - person Neil Traft; 09.03.2011
comment
@Raphael: Что касается принципа замещения, я на самом деле не думаю, что черты должны соответствовать этому принципу. На мой взгляд, это строго для множественного наследования, и именно это отличает черты от наследования. Заимствуя синтаксис Perl, наследование имеет отношение isa, которое должно соответствовать принципу Лискова, тогда как трейты дают больше отношения does, когда вы просто импортируете некоторую функциональность, и не обязательно соблюдение поведенческого контракта. - person Neil Traft; 09.03.2011
comment
@ YH Wong: Если единственное последствие ограничения языка состоит в том, что оно препятствует плохому дизайну, я полностью за это. @Neil Traft: Но в Scala черты также выполняют роль интерфейсов, которые (имхо) также должны быть связаны с is-a. Кроме того, extends и with делают то же самое (для трейтов). Так что я действительно думаю, что в Scala при использовании трейтов следует соблюдать правила Лискова, и это, по сути, означает (среди прочего) вообще избегать конфликтов, даже если они аккуратно разрешены. - person Raphael; 09.03.2011
comment
Вы всегда можете написать ScalaDoc, и это создаст порядок линеаризации для каждого метода в соответствующих документах. - person Daniel C. Sobral; 09.03.2011
comment
@Raphael Рафаэль, я согласен с твоим мнением об интерфейсах. Я определенно не думаю, что трейты следует использовать в качестве интерфейсов. Вот почему я склоняюсь к тому, что черты Scala на самом деле не являются чертами. - person Neil Traft; 09.03.2011
comment
Нил, это не соответствует моему мнению. Я думаю, что вы можете использовать трейты в качестве интерфейсов, так как я думаю, что даже для интерфейсов вы должны избегать конфликтов. Конечно, на уровне языка не возникает конфликта, если вы реализуете одну и ту же подпись для двух интерфейсов с помощью одного метода, но эти интерфейсы могут формулировать разные контракты/спецификации для одной и той же подписи! Такой конфликт даже не может быть обнаружен компилятором (по модулю формальных спецификаций) и, следовательно, является наиболее опасным типом ошибки: если вы предполагаете, что Лисков соблюдается, вы ошибаетесь - молча. - person Raphael; 10.03.2011
comment
@Daniel Отличный комментарий! Обратите внимание, что вам даже не нужно писать какие-либо комментарии к документам (афаик), всегда будет отображаться линеаризация супертипа. - person Raphael; 10.03.2011

Нет, Scala не может переименовывать при импорте.

Интересно, как это вообще сработает. Если метод m признака T переименовать в m2 в объекте o, как будет разрешено p.m, если p является параметром типа T, и через него было передано o?

person Daniel C. Sobral    schedule 09.03.2011
comment
В Squeak, если вы переименовываете импорт, и ни один из ваших других трейтов не имеет метода с такой же сигнатурой, вы должны заново реализовать этот метод самостоятельно. Посмотрите последний пример в этой записи блога, чтобы увидеть типичный вариант использования. - person Neil Traft; 09.03.2011
comment
@Neil Squeak имеет динамический тип, поэтому на самом деле он не применим к этому моменту, который представляет собой проблему статической типизации. - person Daniel C. Sobral; 10.03.2011
comment
Конечно, но я думаю, что у вас было бы подобное требование на любом языке, который позволял бы вам переименовывать методы подобным образом. - person Neil Traft; 11.03.2011