Scala: Абстрактные типы по сравнению с дженериками

Я использовал dbdeploy инструмент от ThoughtWorks в http://dbdeploy.com/ . Это поощряет использование сценариев миграции. Каждый выпуск, мы консолидировали сценарии изменения в единственный файл, чтобы упростить понимание и позволить DBAs 'благословлять' изменения.

242
задан Peter Mortensen 20 May 2013 в 08:54
поделиться

2 ответа

У вас есть хорошая точка зрения по этому поводу здесь:

Назначение системы типов Scala
Беседа с Мартином Одерски, часть III
Билла Веннерса и Фрэнка Соммерса (18 мая 2009 г.)

Обновление (октябрь 2009 г.): нижеследующее фактически проиллюстрировано в этой новой статье Билла Веннерса:
Члены абстрактного типа против параметров универсального типа в Scala (см. резюме в конце)


(Вот соответствующий отрывок из первого интервью, май 2009 г., выделено мной)

Общий принцип

Всегда существовало два понятия абстракции:

  • параметризация и
  • абстрактных членов.

В Java есть и то, и другое, но это зависит от того, над чем вы абстрагируетесь.
В Java у вас есть абстрактные методы, но вы не можете передавать метод в качестве параметра.
У вас нет абстрактных полей, но вы можете передать значение в качестве параметра.
Точно так же у вас нет членов абстрактного типа, но вы можете указать тип в качестве параметра.
Итак, в Java у вас также есть все три из них, но есть различие в том, какой принцип абстракции вы можете использовать для каких вещей. И вы можете возразить, что это различие довольно произвольно.

Путь Scala

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

Почему?

То, что, в частности, абстрактные типы покупают вам, является хорошим решением этих проблем ковариации , о которых мы говорили раньше.
Одна стандартная проблема, которая существует уже долгое время, - это проблема животных и продуктов питания.
Задача заключалась в том, чтобы получить класс Animal с методом eat , который ест немного еды.
Проблема в том, что если у нас есть подкласс Animal и такой класс, как Cow, то они будут есть только траву, а не произвольную пищу. Например, корова не может есть рыбу.
Вы хотите иметь возможность сказать, что у коровы есть метод питания, при котором она ест только траву, а не другие продукты.
На самом деле, вы не можете сделать это в Java, потому что, как выясняется, вы можете создавать ненадежные ситуации, подобные проблеме назначения Fruit переменной Apple, о которой я говорил ранее.

Ответ состоит в том, что вы добавляете абстрактный тип в класс Animal .
Вы говорите, что у моего нового класса Animal есть тип подходящего питания , которого я не знаю.
Так что это абстрактный тип. Вы не даете реализации типа. Тогда у вас есть метод eat , который ест только подходящую пищу .
А затем в классе Cow я бы сказал: ОК, у меня есть Cow, которая расширяет класс Animal , а для типа Cow Подходящее питание равно Grass .
] Итак, абстрактные типы предоставляют это понятие типа в суперклассе, которого я не знаю, который я затем заполняю в подклассах чем-то, что я знаю .

То же самое с параметризацией?

Действительно Вы можете. Вы можете параметризовать класс Animal типом еды, которую он ест.
Но на практике, когда вы делаете это с множеством разных вещей, это приводит к взрыву параметров , и обычно, более того, в границах параметров .
На ECOOP 1998 года Ким Брюс, Фил Уодлер и я опубликовали статью, в которой показали, что по мере увеличения количества вещей, о которых вы не знаете, типичная программа будет расти квадратично .
Итак, есть очень веские причины не создавать параметры, а иметь эти абстрактные элементы, потому что они не дают вам этого квадратичного взрыва.


thatismatt спрашивает в комментариях:

Как вы думаете, следующее является справедливым резюме:

  • Абстрактные типы используются в отношениях «имеет-а» или «использует-а» (например, Корова ест траву )
  • где в качестве родовых обычно подразумеваются отношения «из» (например, Список Ints )

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

  • то, как они используются, и
  • , как управляются границы параметров.

Чтобы понять, о чем говорит Мартин, когда дело доходит до «взрывного роста параметров, и обычно, более того, в границ параметров »и его последующего квадратичного роста при моделировании абстрактного типа с использованием обобщений, вы можете рассмотреть статью« Scalable Component Abstraction », написанную ... Мартином Одерски и Маттиасом Зенгером для OOPSLA 2005, на которые есть ссылки в публикациях проекта Palcom (завершено в 2007 г.).

Соответствующие выдержки

Определение

Члены абстрактного типа обеспечивают гибкий способ абстрагирования над конкретным типы компонентов.
Абстрактные типы могут скрывать информацию о внутреннем устройстве компонента, подобно тому, как они используются в сигнатурах SML . В объектно-ориентированной структуре, где классы могут быть расширены путем наследования, они также могут использоваться в качестве гибкого средства параметризации (часто называемого семейным полиморфизмом, см., Например, запись в веб-журнале , и статью, написанную Эрик Эрнст )

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

абстракция ограниченного типа

abstract class MaxCell extends AbsCell {
type T <: Ordered { type O = T }
def setMax(x: T) = if (get < x) set(x)
}

Здесь объявление типа T ограничено верхней границей типа который состоит из имени класса Ordered и уточнения {type O = T} .
Верхняя граница ограничивает специализацию T в подклассах теми подтипами Ordered, для которых член типа O из равен T .
Из-за этого ограничения метод < класса Ordered гарантированно применим к получателю и аргументу типа T.
Пример показывает, что член ограниченного типа может сам появляться как часть границы.
(т.е. Scala поддерживает F-ограниченный полиморфизм )

(Примечание из статьи Питера Каннинга, Уильяма Кука, Уолтера Хилла, Уолтера Олтоффа:
Ограниченная квантификация была введена Карделли и Вегнером как средство типизации функций, которые действуют единообразно для всех подтипов данного типа.
Они определили простую «объектную» модель и использовали ограниченную количественную оценку для функций проверки типов, которые имеют смысл для всех объектов, имеющих указанный набор «атрибутов».
Более реалистичное представление объектно-ориентированных языков позволит объектам, которые являются элементами рекурсивно определенных типов .
В этом контексте ограниченная количественная оценка больше не служит своей цели. Легко найти функции, которые имеют смысл для всех объектов, имеющих указанный набор методов, но которые нельзя ввести в систему Карделли-Вегнера.
Чтобы обеспечить основу для типизированных полиморфных функций в объектно-ориентированных языках, мы вводим F-ограниченную квантификацию)

Две стороны одной монеты

В языках программирования есть две основные формы абстракции:

  • параметризация и
  • абстрактных членов.

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

Традиционно Java поддерживает параметризацию значений и абстракцию членов для операций. Более поздняя версия Java 5.0 с универсальными шаблонами поддерживает параметризацию также для типов.

Аргументы в пользу включения универсальных шаблонов в Scala двояки:

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

  • Во-вторых, обобщенные и абстрактные типы обычно выполняют разные роли в программах Scala.

    • Обобщения обычно используются, когда требуется только экземпляр типа , тогда как
    • абстрактные типы обычно используются, когда нужно обратиться к абстрактному введите код клиента .
      Последнее возникает, в частности, в двух ситуациях:
    • Кто-то может захотеть скрыть точное определение члена типа от клиентского кода, чтобы получить вид инкапсуляции, известный из модульных систем в стиле SML.
    • Или кто-то может захотеть ковариантно переопределить тип в подклассах, чтобы получить семейный полиморфизм.

В системе с ограниченным полиморфизмом переписывание абстрактного типа в обобщенные типы может повлечь за собой квадратичное расширение границ типов . 1298] Обновление, октябрь 2009 г.

Члены абстрактного типа в сравнении с параметрами универсального типа в Scala (Билл Веннерс)

(выделено мной)

Мои наблюдения относительно членов абстрактного типа заключаются в том, что в первую очередь они являются лучшим выбором, чем параметры универсального типа, когда:

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

Пример:

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

// Type parameter version
class MySuite extends FixtureSuite3[StringBuilder, ListBuffer, Stack] with MyHandyFixture {
  // ...
}

В то время как с подходом к члену типа он будет выглядеть так:

// Type member version
class MySuite extends FixtureSuite3 with MyHandyFixture {
  // ...
}

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

// Type parameter version
class MySuite extends FixtureSuite[StringBuilder] with StringBuilderFixture {
  // ...
}

Они бы не узнали, каково имя параметра типа, указанного как StringBuilder, не найдя его. Им все равно нужно будет выяснить, что означает «параметр фикстуры», но они могут, по крайней мере, получить имя типа, не заглядывая в документацию.

250
ответ дан 23 November 2019 в 03:14
поделиться

У меня был тот же вопрос, когда я читал о Scala.

Преимущество использования дженериков в том, что вы создаете семейство типов. Никому не нужно создавать подкласс Buffer - они могут просто использовать Buffer [Any] , Buffer [String] и т. Д.

Если вы используете абстрактный тип , тогда люди будут вынуждены создать подкласс. Людям понадобятся классы вроде AnyBuffer , StringBuffer и т. Д.

Вам нужно решить, какой из них лучше подходит для ваших конкретных нужд.

37
ответ дан 23 November 2019 в 03:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: