Спочатку термінологічне пояснення: негативні та позитивні позиції походять від логіки. Вони про асиметрію в логічних зв'язок: в поводиться по- різному від . Подібне відбувається і в теорії категорій, де ми говоримо противаріантно і ковариантно замість негативного та позитивного відповідно. У фізиці вони говорять про величини, які поводяться "коваріантно" і "противажно". Тому це дуже загальне явище. Програміст може вважати їх "входом" і "виходом".A BA⇒BAB
Тепер на індуктивні типи даних.
Подумайте індуктивний тип даних в якості свого роду алгебраїчної структури: конструктори операції , які приймають елементи в якості аргументів і виробляють нові елементи . Це дуже схоже на звичайну алгебру: додавання займає два числа і утворює число.T TTTT
В алгебрі прийнято, що операція приймає обмежену кількість аргументів, і в більшості випадків вона бере нульовий (постійний), один (унарний) або два (двійкові) аргументи. Це зручно узагальнити для конструкторів типів даних. Припустимо 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λ⋅vc
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
і вибухнути стек зрештою (на мовах на основі стека).