Злоупотребление алгеброй алгебраических типов данных - почему это работает?

"Алгебраическое" выражение для алгебраических типов данных выглядит очень наводящим на размышления кого-то, у кого есть опыт в математике. Позвольте мне попытаться объяснить, что я имею в виду.

Определив основные типы

  • Продукт
  • Союз +
  • Синглтон X
  • Единица 1

и используя сокращенную для X•X и 2X для X+X et cetera, мы можем тогда определить алгебраические выражения, например. связанные списки

data List a = Nil | Cons a (List a)L = 1 + X • L

и двоичные деревья:

data Tree a = Nil | Branch a (Tree a) (Tree a)T = 1 + X • T²

Теперь, мой первый инстинкт как математик должен сходить с этими выражениями и попытаться решить для L и T. Я мог бы сделать это через повторную замену, но, похоже, гораздо легче злоупотреблять обозначением ужасно и притворяться, что я могу изменить его по своему усмотрению. Например, для связанного списка:

L = 1 + X • L

(1 - X) • L = 1

L = 1 / (1 - X) = 1 + X + X² + X³ + ...

где я использовал расширение степенного ряда 1 / (1 - X) совершенно необоснованным образом, чтобы получить интересный результат, а именно, что тип L либо Nil, либо он содержит 1 элемент, либо содержит 2 элементов или 3 и т.д.

Это становится более интересным, если мы делаем это для двоичных деревьев:

T = 1 + X • T²

X • T² - T + 1 = 0

T = (1 - √(1 - 4 • X)) / (2 • X)

T = 1 + X + 2 • X² + 5 • X³ + 14 • X⁴ + ...

снова, используя расширение степенного ряда (сделано с Wolfram Alpha). Это выражает неочевидный (для меня) факт, что существует только одно двоичное дерево с 1 элементом, 2 двоичных дерева с двумя элементами (второй элемент может быть слева или справа), 5 двоичных деревьев с тремя элементами и т.д..

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

И, возможно, более интересно, можно ли расширить эти идеи? Существует ли теория алгебры типов, которая позволяет, например, произвольные функции на типах, или типы требуют представления степенного ряда? Если вы можете определить класс функций, то имеет ли состав функций какой-либо смысл?

Ответ 1

Отказ от ответственности: многие из них на самом деле не совсем корректны, когда вы учитываете ⊥, поэтому я буду вопиюще игнорировать это ради простоты.

Несколько исходных точек:

  • Обратите внимание, что "объединение", вероятно, не самый лучший термин для A + B здесь - это специально несвязанный союз два типа, потому что две стороны различаются, даже если их типы одинаковы. Для чего это стоит, более общим термином является просто "тип суммы".

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

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

Там еще одна основная операция отсутствует, но я вернусь к этому через мгновение.

Как вы, наверное, заметили, Haskell имеет тенденцию брать понятия из Теории категорий, и все вышеперечисленное имеет очень прямое толкование как таковое:

  • При заданных объектах A и B в Hask их произведение A × B является единственным (с точностью до изоморфизма) типом, который допускает две проекции fst: A × B → A и snd: A × B → B, где заданы любые типы C и функции f: C → A, g: C → B, вы можете определить спаривание f && & g: C → A × B, что fst ∘ (f && g) = f и аналогично для g. Параметричность гарантирует универсальные свойства автоматически, и мой менее чем тонкий выбор имен должен дать вам эту идею. Оператор (&&&) определен в Control.Arrow, кстати.

  • Дуальным из приведенного выше является копроизведение A + B с инъекциями inl: A → A + B и inr: B → A + B, где заданы любые типы C и функции f: A → C, g: B → C, вы можете определить копирование f ||| g: A + B → C, чтобы выполнялись очевидные эквивалентности. Опять-таки, параметричность автоматически гарантирует большую часть сложных элементов. В этом случае стандартные инъекции - это просто Left и Right, а сокрытие - это функция either.

Многие из свойств продукта и типов сумм могут быть получены из вышесказанного. Обратите внимание, что любой одноэлементный тип является терминальным объектом Hask, а любой пустой тип - это начальный объект.

Возвращаясь к вышеупомянутой отсутствующей операции, в декартовой закрытой категории вы экспоненциальные объекты, соответствующие стрелкам категории. Наши стрелки - это функции, наши объекты - типы с видом *, а тип A -> B действительно ведет себя как B A в контексте алгебраического манипулирования типами. Если это не очевидно, почему это должно быть выполнено, рассмотрим тип Bool -> A. Имея только два возможных входа, функция этого типа изоморфна двум значениям типа A, т.е. (A, A). Для Maybe Bool -> A мы имеем три возможных входа и т.д. Кроме того, заметим, что если мы перефразируем вышеописанное определение копарирования для использования алгебраических обозначений, получим тождество C A × C B= C A + B.

Что касается того, почему все это имеет смысл, и, в частности, почему ваше использование расширения степенного ряда оправдано, обратите внимание, что большая часть из вышеизложенного относится к "обитателям" типа (т.е. различным значениям этого типа), чтобы продемонстрировать алгебраическое поведение. Чтобы сделать эту перспективу явной:

  • Тип продукта (A, B) представляет значение, каждое из которых A и B, взятое независимо. Поэтому для любого фиксированного значения a :: A для каждого жителя B существует одно значение типа (A, B). Это, конечно, декартово произведение, а число жителей типа продукта является продуктом количества жителей факторов.

  • Тип суммы Either A B представляет значение из A или B, причем левая и правая ветки различаются. Как упоминалось ранее, это дизъюнктное объединение, а число обитателей типа суммы - это сумма числа обитателей слагаемых.

  • Экспоненциальный тип B -> A представляет собой отображение значений типа B в значения типа A. Для любого фиксированного аргумента b :: B ему может быть присвоено любое значение A; значение типа B -> A выбирает одно такое отображение для каждого входа, которое эквивалентно произведению множества копий A как B имеет обитателей, следовательно, возведение в степень.

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

С другой стороны, приведенные выше конструкции тратят много времени на подсчет жителей, и перечисление возможных значений типа является полезной концепцией здесь. Это быстро приводит нас к перечисляющей комбинаторике, и если вы обратитесь к связанной статье Википедии, вы обнаружите, что одна из первых вещей, которую она делает, пары "и" союзы "в том же смысле, что и типы продуктов и сумм с помощью генерирующих функций, то делает то же самое для "последовательностей", что идентичны спискам Haskell, используя точно такую ​​же технику, что и вы.


Изменить: О, и здесь быстрый бонус, который, я думаю, демонстрирует поразительно. Вы упомянули в комментарии, что для дерева типа T = 1 + T^2 вы можете получить идентификатор T^6 = 1, что явно неверно. Однако T^7 = T выполняется, и биекция между деревьями и семью кортежами деревьев может быть построена напрямую, ср. Андреас Бласс "Семь деревьев в одном" .

Изменить × 2: В отношении конструкции "производного типа", упомянутой в других ответах, вы также можете наслаждаться этот документ от того же автора, который основывается на идее дальше, включая понятия разделения и другие интересные вещи.

Ответ 2

Двоичные деревья определяются уравнением T=1+XT^2 в полукольце типов. По построению, T=(1-sqrt(1-4X))/(2X) определяется тем же уравнением в полукольце комплексных чисел. Поэтому, учитывая, что мы решаем одно и то же уравнение в одном и том же классе алгебраической структуры, на самом деле не удивительно, что мы видим некоторые сходства.

Вывод состоит в том, что, когда мы рассуждаем о многочленах в полукольце комплексных чисел, мы обычно используем тот факт, что комплексные числа образуют кольцо или даже поле, поэтому мы оказываемся с использованием таких операций, как вычитание, которые не относятся к полукольцам, Но мы часто можем исключить вычитания из наших аргументов, если у нас есть правило, которое позволяет отменить с обеих сторон уравнения. Это то, о чем свидетельствует Fiore и Leinster, показывающее, что многие аргументы о кольцах могут быть переданы в полукольцы.

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

Однако, больше, чем история. Одно дело доказать два типа равны (скажем), показывая два степенных ряда равны. Но вы также можете выводить информацию о типах, проверяя условия в силовой серии. Я не уверен, что должно быть официальным выражением. (Я рекомендую Brent Yorgey на комбинаторных видах для некоторой работы, тесно связанной между собой, но виды не такие, как типы.)

То, что я нахожу совершенно безумным, заключается в том, что то, что вы обнаружили, можно расширить до исчисления. Теоремы об исчислении могут быть перенесены на полукольцо типов. На самом деле даже аргументы о конечных разностях могут быть перенесены, и вы обнаружите, что классические теоремы из численного анализа имеют интерпретации в теории типов.

Удачи!

Ответ 3

Кажется, что все, что вы делаете, - это расширение отношения повторения.

L = 1 + X • L
L = 1 + X • (1 + X • (1 + X • (1 + X • ...)))
  = 1 + X + X^2 + X^3 + X^4 ...

T = 1 + X • T^2
L = 1 + X • (1 + X • (1 + X • (1 + X • ...^2)^2)^2)^2
  = 1 + X + 2 • X^2 + 5 • X^3 + 14 • X^4 + ...

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

Ответ 4

У меня нет полного ответа, но эти манипуляции имеют тенденцию "просто работать". Соответствующая статья может быть Объекты категорий как комплексные числа от Fiore и Leinster - я столкнулся с этим при чтении блог sigfpe по связанным темам; остальная часть этого блога - золотая руда для подобных идей и стоит проверить!

Вы также можете дифференцировать типы данных, кстати - это даст вам подходящую Zipper для типа данных!

Ответ 5

Алгебра коммуникационных процессов (ACP) занимается аналогичными выражениями для процессов. Он предлагает добавление и умножение в качестве операторов для выбора и последовательности с соответствующими нейтральными элементами. На их основе есть операторы для других конструкций, таких как parallelism и сбой. См. http://en.wikipedia.org/wiki/Algebra_of_Communicating_Processes. Существует также документ онлайн под названием "Краткая история алгебры процесса".

Я работаю над расширением языков программирования с помощью ACP. В апреле прошлого года я представил исследовательскую работу на Scala Days 2012, доступную по адресу http://code.google.com/p/subscript/

На конференции я продемонстрировал отладчик, выполняющий параллельную рекурсивную спецификацию пакета:

Bag = A; (Сумка & а)

где A и позиция для действий ввода и вывода; точки с запятой и амперсанды обозначают последовательность и parallelism. Смотрите видео на странице SkillsMatter, доступной по предыдущей ссылке.

Спецификация мешка, более сопоставимая с

L = 1 + X • L

будет

B = 1 + X & B

ACP определяет parallelism в терминах выбора и последовательности с использованием аксиом; см. статью в Википедии. Интересно, что такое суммарная аналогия для

L = 1/(1-X)

Программирование в стиле ACP удобно для текстовых парсеров и GUI-контроллеров. Технические характеристики, такие как

searchCommand = нажал (searchButton) + ключ (Enter)

cancelCommand = нажал (cancelButton) + клавиша (Escape)

можно записать более кратко, сделав два уточнения "нажатыми" и "ключевыми" неявными (например, что позволяет Scala с функциями). Следовательно, мы можем написать:

searchCommand = searchButton + Enter

cancelCommand = cancelButton + Escape

Теперь правые части содержат операнды, которые являются данными, а не процессами. На этом уровне нет необходимости знать, какие неявные уточнения превратят эти операнды в процессы; они не обязательно будут уточнять входные действия; будут также применяться выходные действия. в спецификации тестового робота.

Процессы получают такие данные как спутники; таким образом, я использую термин "алгебра элементов".

Ответ 6

Серия Calculus и Maclaurin с типами

Вот еще одно небольшое дополнение - комбинаторное понимание того, почему коэффициенты в расширении серии должны "работать", в частности, фокусируясь на рядах, которые могут быть получены с использованием подхода Тейлора-Маклорена из исчисления. NB: примерное расширение серии, которое вы указываете для типа управляемого типа, - это серия Maclaurin.

Поскольку другие ответы и комментарии касаются поведения выражений алгебраического типа (суммы, продукты и показатели), этот ответ будет устранять эту деталь и сосредоточиться на типе "исчисление".

Вы можете заметить, что в этом ответе на кавычки попали кавычки. Есть две причины:

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

Определение серии Маклорена

Ряд Маклорена функции f: ℝ → ℝ определяется как

f(0) + f'(0)X + (1/2)f''(0)X² + ... + (1/n!)f⁽ⁿ⁾(0)Xⁿ + ...

где f⁽ⁿ⁾ означает n ю производную от f.

Чтобы понять смысл серии Maclaurin, интерпретируемой с помощью типов, нам нужно понять, как мы можем интерпретировать три вещи в контексте типа:

  • (возможно, многократное) производное
  • применяя функцию к 0
  • такие термины, как (1/n!)

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

Что я подразумеваю под "подходящим коллегой"? Он должен иметь аромат изоморфизма - если мы сможем сохранить правду в обоих направлениях, то факты, выводимые в одном контексте, могут быть перенесены на другой.

Исчисление с типами

Итак, что означает производная выражения типа? Оказывается, что для большого и хорошо организованного ("дифференцируемого") класса выражений и функторов типа существует естественная операция, которая ведет себя аналогично достаточно, чтобы быть подходящей интерпретацией!

Чтобы испортить пуанлинг, операция, аналогичная дифференциации, заключается в создании "одноточечных контекстов". Это прекрасное место для дальнейшего расширения этой конкретной точки, но базовая концепция одноточечного контекста (da/dx) заключается в том, что он представляет собой результат извлечения одного подтипа определенного типа (x) из члена (из тип a), сохраняя всю другую информацию, в том числе необходимую для определения исходного местоположения подпункта. Например, один из способов представления контекста с одним отверстием для списка состоит из двух списков: один для элементов, которые были до него извлечены, и один для элементов, которые пришли после.

Мотивация идентификации этой операции с дифференциацией исходит из следующих наблюдений. Мы пишем da/dx как тип одноточечных контекстов для типа a с отверстием типа x.

d1/dx = 0
dx/dx = 1
d(a + b)/dx = da/dx + db/dx
d(a × b)/dx = a × db/dx + b × da/dx
d(g(f(x))/dx = d(g(y))/dy[f(x)/a] × df(x)/dx

Здесь 1 и 0 представляют типы с ровно одним и ровно нулевыми жителями соответственно, а + и × представляют собой суммы и типы продуктов, как обычно. f и g используются для представления функций типа или типов формирователей выражений, а [f(x)/a] означает операцию подстановки f(x) для каждого a в предыдущем выражении.

Это может быть записано в стиле без точек, написание f' означает производную функцию функции типа f, таким образом:

(x ↦ 1)' = x ↦ 0
(x ↦ x)' = x ↦ 1
(f + g)' = f' + g'
(f × g)' = f × g' + g × f'
(g ∘ f)' = (g' ∘ f) × f'

что может быть предпочтительным.

NB, равенства можно сделать строгими и точными, если мы определяем производные с использованием классов изоморфизма типов и функторов.

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

Теперь мы можем интерпретировать дифференцирование, что означает n я производная выражения типа dⁿe/dxⁿ? Это тип, представляющий контексты n -place: термины, которые, когда "заполнены" n терминами типа x дают e. Существует еще одно ключевое наблюдение, связанное с " (1/n!) ", Которое будет позже.

Инвариантная часть функтора типа: применение функции к 0

У нас уже есть интерпретация для 0 в мире типов: пустой тип без членов. Что это значит, с комбинаторной точки зрения, применить к нему функцию типа? В более конкретных терминах, предполагая, что f является функцией типа, как выглядит f(0)? Ну, у нас, конечно, нет доступа к чему-либо типа 0, поэтому любые конструкции f(x) которые требуют x, недоступны. Остаются те термины, которые доступны в их отсутствие, которые мы можем назвать "инвариантной" или "постоянной" частью типа.

Для явного примера возьмем функтор Maybe, который можно представить алгебраически как x ↦ 1 + x. Когда мы применяем это к 0, получаем 1 + 0 - это точно так же, как 1: единственным возможным значением является значение None. Для списка аналогичным образом мы получаем только термин, соответствующий пустому списку.

Когда мы возвращаем его и интерпретируем тип f(0) как число, его можно рассматривать как число того, сколько членов типа f(x) (для любого x) можно получить без доступа к x: то есть, число "пустых" терминов.

Объединение: полная интерпретация серии Маклорена

Боюсь, я не могу придумать подходящую прямую интерпретацию (1/n!) Как типа.

Если мы рассмотрим, однако, тип f⁽ⁿ⁾(0) в свете вышеизложенного, мы видим, что его можно интерпретировать как тип n -place контекстов для термина типа f(x) который еще не содержат x - то есть, когда мы "интегрируем" их n раз, результирующий член имеет ровно n x s, не более, не меньше. Тогда интерпретация типа f⁽ⁿ⁾(0) как числа (как и в коэффициентах ряда Маклорена f) является просто числом таких пустых n -place контекстов. Мы почти там!

Но где заканчивается (1/n!)? Изучение процесса дифференциации типа показывает, что при многократном применении он сохраняет "порядок", в котором извлекаются субтермы. Например, рассмотрим термин (x₀, x₁) типа x × x и операцию "сделать дыру" в нем дважды. Мы получаем обе последовательности

(x₀, x₁)  ↝  (_₀, x₁)  ↝  (_₀, _₁)
(x₀, x₁)  ↝  (x₀, _₀)  ↝  (_₁, _₀)
(where _ represents a 'hole')

хотя оба происходят с одного и того же термина, потому что есть 2! = 2 2! = 2 способа взять два элемента из двух, сохраняя порядок. В общем, есть n! способы взять n элементов из n. Поэтому для того, чтобы получить счетчик числа конфигураций типа функтора, у которых есть n элементов, мы должны считать тип f⁽ⁿ⁾(0) и делить на n! точно так же, как и в коэффициентах ряда Маклорена.

Итак, разделив на n! оказывается интерпретируемым просто как сам.

Заключительные мысли: "рекурсивные" определения и аналитичность

Во-первых, некоторые наблюдения:

  • если функция f: ℝ → ℝ имеет производную, эта производная является единственной
  • аналогично, если функция f: ℝ → ℝ аналитична, то имеет ровно один соответствующий полиномиальный ряд

Поскольку у нас есть цепное правило, мы можем использовать неявное дифференцирование, если мы формализуем производные типа как классы изоморфизма. Но неявное дифференцирование не требует каких-либо чужих маневров, таких как вычитание или деление! Поэтому мы можем использовать его для анализа рекурсивных определений типов. Чтобы взять ваш пример списка, мы имеем

L(X) ≅ 1 + X × L(X)
L'(X) = X × L'(X) + L(X)

и тогда мы можем оценить

L'(0) = L(0) = 1

для получения коэффициента в серии Маклорена.

Но поскольку мы уверены, что эти выражения действительно строго "дифференцируемы", если только неявно, и поскольку мы имеем соответствие с функциями ℝ → ℝ, где производные, безусловно, уникальны, мы можем быть уверены, что даже если мы получим значения, результат недействителен.

Точно так же, чтобы использовать второе наблюдение из-за соответствия (это гомоморфизм?) С функциями ℝ → ℝ, мы знаем, что при условии, что мы удовлетворены тем, что функция имеет ряд Маклорена, если мы можем найти любые ряды в все, изложенные выше принципы могут применяться, чтобы сделать его строгим.

Что касается вашего вопроса о составе функций, я полагаю, что правило цепи дает частичный ответ.

Я не уверен, как много ADTs типа Haskell, к которому это относится, но я подозреваю, что это много, если не все. Я обнаружил поистине чудесное доказательство этого факта, но этот запас слишком мал, чтобы сдержать его...

Теперь, конечно, это только один способ выработать то, что происходит здесь, и, вероятно, есть много других способов.

Основная информация: TL; DR

  • тип "дифференциация" соответствует " созданию дыры ".
  • применение функтора к 0 дает нам "пустые" члены этого функтора.
  • Поэтому ряд Маклорена (несколько) строго соответствует перечислению числа членов типа функтора с определенным числом элементов.
  • неявное дифференцирование делает это более водонепроницаемым.
  • уникальность производных и единственность степенных рядов означают, что мы можем подделывать детали, и это работает.

Ответ 7

Теория зависимого типа и функции произвольного типа

Мой первый ответ на этот вопрос был высоким по концепциям и низким деталям и отражался на подтексте, "что происходит?"; этот ответ будет таким же, но сфокусирован на подзапросе, "можем ли мы получить функции произвольного типа?".

Одним расширением алгебраических операций суммы и произведения являются так называемые "большие операторы", которые представляют собой сумму и произведение последовательности (или, более общо, сумму и произведение функции над областью), обычно записываемой Σ и Π соответственно. См. Обозначение Sigma.

Итак, сумма

a₀ + a₁X + a₂X² + ...

может быть записано

Σ[i ∈ ℕ]aᵢXⁱ

где a - некоторая последовательность действительных чисел, например. Продукт будет представлен аналогично Π вместо Σ.

Когда вы смотрите издалека, это выражение выглядит во многом как "произвольная" функция в X; мы, конечно, ограничены выражаемыми рядами и связанными с ними аналитическими функциями. Является ли это кандидатом на представление в теории типов? Определенно!

Класс теорий типов, которые имеют непосредственные представления этих выражений, является классом "зависимых" типов теорий: теории с зависимыми типами. Естественно, у нас есть термины, зависящие от терминов, а также в таких языках, как Haskell с функциями типа и количественной количественной оценкой, терминов и типов в зависимости от типов. В зависимой настройке мы дополнительно имеем типы, зависящие от термов. Haskell не является зависимым от языка, хотя многие функции зависимых типов можно моделировать пытаясь немного перепутать язык.

Curry-Howard и зависимые типы

"Изоморфизм Карри-Говарда" начал жизнь как наблюдение за тем, что термины и правила суждения типа просто типизированного лямбда-исчисления точно соответствуют естественному вычету (как сформулировано Гентценом), примененному к интуиционистской логике высказываний, причем типы, принимающие место предложений и термины, заменяющие доказательства, несмотря на то, что они были независимо изобретены/обнаружены. С тех пор это был огромный источник вдохновения для теоретиков типа. Одной из наиболее очевидных вещей для рассмотрения является вопрос о том, может ли это соответствие логики высказываний распространяться на логику предиката или более высокого порядка. Теории зависимых типов первоначально возникли из этого пути исследования.

Для введения в изоморфизм Карри-Говарда для просто типизированного лямбда-исчисления см. здесь. В качестве примера, если мы хотим доказать A ∧ B, мы должны доказать a и доказать B; комбинированное доказательство - это просто пара доказательств: по одному для каждого конъюнкта.

В естественном выводе:

Γ ⊢ A    Γ ⊢ B
Γ ⊢ A ∧ B

и в просто типизированном лямбда-исчислении:

Γ ⊢ a : A    Γ ⊢ b : B
Γ ⊢ (a, b) : A × B

Аналогичные соответствия существуют для и типов сумм, и типов функций, а также различных правил исключения.

Недоказуемое (интуиционистски ложное) предложение соответствует необитаемому типу.

Имея в виду аналогию типов как логических предложений, мы можем начать рассматривать, как моделировать предикаты в типе-мире. Существует много способов, которыми это было формализовано (см. это введение в теорию интуиционистского типа Мартина-Лёфа для широко используемого стандарта), но абстрактный подход обычно замечает, что предикат подобен предложению со свободными термами, или, альтернативно, функции, переводящей понятия в предложения. Если мы разрешаем выражениям типов содержать термины, то обработка в стиле лямбда-исчисления сразу представляется как возможность!

Учитывая только конструктивные доказательства, что составляет доказательство ∀x ∈ X.P(x)? Мы можем рассматривать это как доказательную функцию, беря слагаемые (X) к доказательствам их соответствующих предложений (P(x)). Итак, члены (доказательства) типа (предложения) ∀x : X.P(x) являются "зависимыми функциями", которые для каждого X в X дают член типа P(x).

Как насчет ∃x ∈ X.P(x)? Нам нужен любой член X, X, вместе с доказательством P(x). Итак, членами (доказательствами) типа (предложения) ∃x : X.P(x) являются "зависимые пары": выделенный член X в X вместе с термином типа P(x).

Обозначения: Я буду использовать

∀x ∈ X...

для фактических утверждений о членах класса X и

∀x : X...

для выражений типа, соответствующих универсальной квантификации по типу X. Аналогично для .

Комбинаторные соображения: произведения и суммы

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

Я буду использовать обозначение модуля

|A|

чтобы представить "размер" типа a, чтобы четко указать соответствие, указанное в вопросе, между типами и числами. Обратите внимание, что это понятие вне теории; Я не утверждаю, что в этом языке должен быть такой оператор.

Рассчитаем возможные (полностью уменьшенные, канонические) члены типа

∀x : X.P(x)

который является типом зависимых функций, принимающих члены X типа X терминам типа P(x). Каждая такая функция должна иметь выход для каждого члена X, и этот вывод должен быть определенного типа. Для каждого X в X, то это дает |P(x)| "выбор" вывода.

Перфорация

|∀x : X.P(x)| = Π[x : X]|P(x)|

который, конечно, не имеет большого смысла, если X IO (), но применим к алгебраическим типам.

Аналогично, термин типа

∃x : X.P(x)

- тип пар (x, p) с p : P(x), поэтому для любого X в X мы можем построить подходящую пару с любым членом из P(x), давая |P(x)| 'choice'.

Следовательно,

|∃x : X.P(x)| = Σ[x : X]|P(x)|

с теми же оговорками.

Это оправдывает общие обозначения для зависимых типов в теориях с использованием символов Π и Σ, и действительно, многие теории размывают различие между "для всех" и "продуктом" и между "есть" и "сумма", из-за вышеупомянутых соответствий.

Мы приближаемся!

Векторы: представляющие зависимые кортежи

Можем ли мы теперь кодировать числовые выражения, например

Σ[n ∈ ℕ]Xⁿ

как выражения типов?

Не совсем. Хотя мы можем неофициально рассматривать значение выражений типа Xⁿ в Haskell, где X - тип и n натуральное число, это злоупотребление нотацией; это выражение типа, содержащее число: явно не допустимое выражение.

С другой стороны, с зависимыми типами изображения, типы, содержащие числа, являются именно точкой; на самом деле, зависимые кортежи или "векторы" являются очень часто приводимым примером того, как зависимые типы могут обеспечить прагматическую безопасность на уровне типа для операций, таких как доступ к списку. Вектор - это всего лишь список вместе с информацией типа на его длину: именно то, что мы будем использовать для выражений типа, например Xⁿ.

В течение всего этого ответа пусть

Vec X n

- тип векторов длины - n значений типа X.

Технически n здесь, а не фактическое натуральное число, есть представление в системе натурального числа. Мы можем представить натуральные числа (Nat) в стиле Пеано как либо нуль (0), либо преемник (S) другого натурального числа, а для n ∈ ℕ пишем ˻n˼ как означающий термин в Nat который представляет n. Например, ˻3˼ - S (S (S 0)).

Тогда имеем

|Vec X ˻n˼| = |X|ⁿ

для любого n ∈ ℕ.

Типы Nat: продвижение терминов к типам

Теперь мы можем кодировать выражения типа

Σ[n ∈ ℕ]Xⁿ

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

Один заключительный фрагмент головоломки для "произвольных" функций - это то, как кодировать, для

f : ℕ → ℕ

выражения, такие как

Σ[n ∈ ℕ]f(n)Xⁿ

так что мы можем применить произвольные коэффициенты к степенному ряду.

Мы уже понимаем соответствие алгебраических типов с числами, позволяя нам отображать от типов к числам и функциям типа к числовым функциям. Мы также можем пойти другим путем! - принимая естественное число, очевидно, существует определяемый алгебраический тип с множеством членов-членов, независимо от того, имеются ли у нас зависимые типы. Мы можем легко доказать это вне теории типов индукцией. Нам нужен способ отображения от натуральных чисел к типам внутри системы.

Удовлетворительная реализация заключается в том, что, как только у нас есть зависимые типы, доказательство индукции и конструирования рекурсией становится близким - действительно, это то же самое во многих теориях. Поскольку по индукции мы можем доказать, что существуют типы, которые удовлетворяют наши потребности, мы не сможем их построить?

Существует несколько способов представления типов на уровне термина. Я буду использовать здесь мнимую нотацию Haskellish с * для универсума типов, сам по себе обычно считается типом в зависимой настройке. 1

Аналогичным образом существует также по крайней мере так много способов обозначить ' -elimination', поскольку существуют теории зависимых типов. Я буду использовать Haskellish-образцовую нотацию.

Нам нужно отображение, α от Nat до *, со свойством

∀n ∈ ℕ.|α ˻n˼| = n.

Достаточно следующее псевдоопределение.

data Zero -- empty type
data Successor a = Z | Suc a -- Successor ≅ Maybe

α : Nat -> *
α 0 = Zero
α (S n) = Successor (α n)

Итак, мы видим, что действие α отражает поведение преемника S, что делает его своего рода гомоморфизмом. Successor - это функция типа, которая "добавляет один" к числу членов типа; то есть |Successor a| = 1 + |a| для любого a с определенным размером.

Например, α ˻4˼ (который является α (S (S (S (S 0))))),

Successor (Successor (Successor (Successor Zero)))

и члены этого типа

Z
Suc Z
Suc (Suc Z)
Suc (Suc (Suc Z))

дает нам ровно четыре элемента: |α ˻4˼| = 4.

Аналогично, для любого n ∈ ℕ имеем

|α ˻n˼| = n

по мере необходимости.

  • Для многих теорий члены * являются просто представителями типов, а операция предоставляется как явное сопоставление от терминов типа * к связанным с ними типам. Другие теории позволяют самим литеральным типам быть объектами на уровне терминов.

"Произвольные" функции?

Теперь у нас есть аппарат, чтобы выразить полностью общий степенной ряд как тип!

Серия

Σ[n ∈ ℕ]f(n)Xⁿ

становится типом

∃n : Nat.α (˻f˼ n) × (Vec X n)

где ˻f˼ : Nat → Nat - некоторое подходящее представление в языке функции f. Мы можем видеть это следующим образом.

|∃n : Nat.α (˻f˼ n) × (Vec X n)|
    = Σ[n : Nat]|α (˻f˼ n) × (Vec X n)|          (property of ∃ types)
    = Σ[n ∈ ℕ]|α (˻f˼ ˻n˼) × (Vec X ˻n˼)|        (switching Nat for ℕ)
    = Σ[n ∈ ℕ]|α ˻f(n)˼ × (Vec X ˻n˼)|           (applying ˻f˼ to ˻n˼)
    = Σ[n ∈ ℕ]|α ˻f(n)˼||Vec X ˻n˼|              (splitting product)
    = Σ[n ∈ ℕ]f(n)|X|ⁿ                           (properties of α and Vec)

Просто, как это "произвольно"? Этот метод ограничивается не только целыми коэффициентами, но и натуральными числами. Кроме того, f может быть вообще чем угодно, учитывая Turing Complete с зависимыми типами, мы можем представить любую аналитическую функцию с естественным числовые коэффициенты.

Я не исследовал его взаимодействие с, например, случаем, указанным в вопросе List X ≅ 1/(1 - X), или каким возможным чувством могут иметь такие отрицательные и нецелые "типы" в этом контексте.

Надеюсь, этот ответ будет каким-то образом исследовать, как далеко мы можем идти с произвольными функциями типа.