Сначала терминологическое объяснение: отрицательные и положительные позиции исходят из логики. Они об асимметрии в логических связок: в ведет себяразному от 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в конечном итоге привести к бесконечному расширению и взрыву стека (в языках на основе стека).