Сначала терминологическое объяснение: отрицательные и положительные позиции исходят из логики. Они об асимметрии в логических связок: в ведет себяразному от B . Похожая вещь происходит в теории категорий, где мы говоримконтравариантныйиковариантныйвместо отрицательного и положительного соответственно. В физике они говорят о величинах, которые ведут себя «ковариантно» и «тоже контравариантно». Так что это очень общее явление. Программист может думать о них как о «входе» и «выходе».A ⇒ BAВ
Теперь на индуктивные типы данных.
Подумайте индуктивный тип данных в качестве своего рода алгебраической структуры: конструкторы операции , которые принимают элементы T в качестве аргументов и производят новые элементы T . Это очень похоже на обычную алгебру: сложение берет два числа и производит число.TTT
В алгебре принято, что операция принимает конечное число аргументов, и в большинстве случаев она принимает ноль (постоянный), один (унарный) или два (двоичных) аргумента. Это удобно обобщить для конструкторов типов данных. Предположим c
, это конструктор для типа данных T
:
- Если
c
это константа, мы можем думать о ней как о функции unit -> T
, или эквивалентно (empty -> T) -> T
,
- если
c
унарный, мы можем думать о нем как о функции T -> T
или, что то же самое (unit -> T) -> T
,
- если
c
бинарный, мы можем думать о нем как о функции T -> T -> T
, или эквивалентно T * T -> T
, или эквивалентно (bool -> T) -> T
,
- если бы мы хотели конструктор,
c
который принимает семь аргументов, мы могли бы рассматривать его как функцию, (seven -> T) -> T
где seven
есть некоторый ранее определенный тип с семью элементами.
- у нас также может быть конструктор,
c
который принимает бесконечно много аргументов, это будет функция (nat -> T) -> T
.
Эти примеры показывают, что общая форма конструктора должна быть
c : (A -> T) -> T
куда мы звоним A
на Арность о c
и мы думаем, c
как конструктор , который принимает A
-many аргументов типа T
для получения элемента T
.
Здесь есть кое-что очень важное: арность должна быть определена до того, как мы ее определим T
, иначе мы не можем сказать, что должны делать конструкторы. Если кто-то пытается получить конструктор
broken: (T -> T) -> T
тогда вопрос "сколько аргументов broken
нужно?" не имеет хорошего ответа. Вы можете попытаться ответить на него T
словами «требуется множество аргументов», но это не сработает, поскольку T
еще не определено. Мы могли бы попытаться выбраться из кунундрума, используя причудливую теорию с фиксированной точкой, чтобы найти тип T
и инъективную функцию (T -> T) -> T
, и добились бы успеха, но мы также нарушили бы принцип индукции на T
этом пути. Так что это просто плохая идея попробовать такую вещь.
Ради полноты позвольте мне объяснить всю историю. Нам нужно немного обобщить приведенную выше форму конструкторов. Иногда у нас есть операции или конструкторы, которые принимают параметры . Например, для скалярного умножения требуется скаляр и вектор v, чтобы получить вектор λ ⋅ v . Это одинарная операция над векторами, параметризованная скаляром. Мы могли быλvλ ⋅ v смотреть скалярное умножение бесконечно много одинарных операций, по одному для каждого скаляра, но это раздражает. Итак, общая форма конструктора c
должна позволять параметр некоторого типа B
:
c : B * (A -> T) -> T
Действительно, многие конструкторы можно переписать таким образом, но не все, нам нужно еще один шаг, а именно мы должны позволить , A
чтобы зависеть от B
:
c : (∑ (x : B), A x -> T) -> T
Это окончательная форма конструктора для индуктивного типа. Это также точно, что W-типы. Форма настолько общая, что нам нужен только один конструкторc
! Действительно, если у нас есть два из них
d' : (∑ (x : B'), A' x -> T) -> T
d'' : (∑ (x : B''), A'' x -> T) -> T
тогда мы можем объединить их в один
d : (∑ (x : B), A x -> T) -> T
где
B := B' + B''
A(inl x) := A' x
A(inr x) := A'' x
Кстати, если мы карри общего вида, мы видим, что это эквивалентно
c : ∏ (x : B), ((A x -> T) -> T)
что ближе к тому, что люди на самом деле записывают в виде помощников. Помощники по проверке позволяют нам записывать конструкторы удобным способом, но они эквивалентны общей форме выше (упражнение!).
A
в конечном итоге привести к бесконечному расширению и взрыву стека (в языках на основе стека).