Можно ли объединить понятия наследования и параметрического полиморфизма? - PullRequest
5 голосов
/ 18 июля 2011

Интересно, возможно ли вообще объединить понятия наследования и параметрического полиморфизма («дженерики»), особенно в отношении дисперсии, а также в терминах, как («синтаксис») и где (use-site / объявление-site) они должно быть определено?

Рассмотрим эту точку зрения:

  • Подтип e. г. S <: T может восприниматься как ко-вариантное поведение, поскольку входные аргументы, принимающие T, также будут принимать S.
  • Изменение «дисперсии модели наследования» на инвариант возможно только на стороне определения, если запретить подтипирование (например, добавление модификатора final к определению класса), контр-дисперсия невозможна, насколько я могу видели в большинстве случаев
  • Параметрический полиморфизм по умолчанию инвариантен, но может быть сделан ко- / контра-вариант

Кажется, есть несущественное несоответствие между концепциями, учитывая

  • языки боли возникли, допустив «небезопасную» ковариацию (например, String[] <: Object[] в Java / C #)
  • различия в том, как наследование / параметрический полиморфизм объявляется и используется по сравнению с наследованием

В некоторых языках видно, что оба прекрасно работают вместе, как, например,

class Foo extends Ordered[Foo]

для реализации порядка упорядочивания / сравнения.

  • Возможно ли, что понятия наследования и параметрического полиморфизма могут быть унифицированы и иметь одинаковое поведение по умолчанию (например, ковариация по умолчанию или это приведет к необходимости помечать большинство типов аннотацией инвариантности вместо этого, поэтому просто перемещать уродство в другой момент)? Будет ли это более практичным, как если бы структуры данных тоже стали неизменными по умолчанию?
  • Существует ли формальная система, в которой это доказало свою надежность?
  • Какие параметры / изменения синтаксиса наиболее вероятны, независимо от конкретного языка программирования?
  • Есть ли какой-нибудь рабочий пример или язык, где это / что-то подобное уже работает?

1 Ответ

4 голосов
/ 18 июля 2011

Под ковариацией / контравариантностью обычно подразумевают это.Предположим, что X, Y, Z являются типами.Предположим далее, что a → b обозначает тип функции с аргументом типа a и результатом типа b.<: обозначает отношение подтипа или, возможно, какое-то другое понятие «соответствия».Стрелка ⇒ гласит «влечет за собой».Тогда справедливо следующее:

X <: Y ⇒ (Z → X) <: (Z → Y)
X <: Y ⇒ (Y → Z) <: (X → Z)

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

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

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

Одна теоретическая работа, которую должен прочитать каждый начинающий разработчик языка, - «Теория объектов» Луки Карделли.

...