Зловживання алгеброю алгебраїчних типів даних - чому це працює?


289

Вираз «алгебраїка» для алгебраїчних типів даних виглядає дуже суттєвим для тих, хто має досвід математики. Дозвольте спробувати пояснити, що я маю на увазі.

Визначивши основні типи

  • Товар
  • Союз +
  • Сінглтон X
  • Одиниця 1

і використовуючи скорочену для X•Xі 2Xдля X+Xі так далі, ми можемо визначити алгебраїчні вирази , наприклад , пов'язані списки

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 двійкових дерев з трьома елементами тощо .

Тож моє запитання - що я тут роблю? Ці операції здаються невиправданими (який саме квадратний корінь типу алгебраїчних даних?), Але вони призводять до розумних результатів. чи має значення коефіцієнт двох алгебраїчних типів даних в інформатиці, чи це лише нотаріальна хитрість?

І, можливо, що цікавіше, чи можна поширити ці ідеї? Чи існує теорія алгебри типів, яка дозволяє, наприклад, довільні функції на типи, чи потрібні типи представлення рядів потужності? Якщо ви можете визначити клас функцій, то чи має склад функцій якесь значення?


19
Ви можете знайти це цікаве / відповідне: blog.lab49.com/archives/3011
shang

4
Не, якщо він зберігає дані у кожному вузлі. Це або схоже, Branch x (Branch y Nil Nil) Nilабо схоже Branch x Nil (Branch y Nil Nil).
Кріс Тейлор

4
@nlucaroni: bottom - це значення, а не тип. Справжній нульовий тип не матиме жодних значень цього типу, що неможливо в Haskell, якщо ви не будете ігнорувати днища. Якщо ви враховуєте найнижчі значення, то типи, що містять лише днища, стають типом одиниці, яка ... не корисна більшість часу, а також багато інших речей.
CA McCann

3
Я погоджуюсь, що це звичайна практика Хаскелла, її все ще глупо. А саме, це означає, що ми використовуємо "дно" по-різному, ніж вони роблять в теорії логіки і типу, що мені здається поганим. Дивлячись на те ж саме з чистого коду, це не робить їх однаковими: "Впоратися з незграбним загоном" дає зрозуміти, що в семантиці Хаскелла є ціла низка "поганих значень", які зациклюються назавжди і кидають виняток, явно не однакові . Заміна однієї іншої не є коректною еквівалентною міркуванням. Haskell має словниковий запас для опису цих поганих значень undefined, throwі т.д. Ми повинні використовувати його.
Philip JF

17
Моє розум був роздутий цим питанням
TheIronKnuckle

Відповіді:


138

Відмова від відповідальності: Багато чого насправді не спрацьовує правильно, якщо ви рахуєте ⊥, тому я просто не зважаю на це заради простоти.

Кілька початкових пунктів:

  • Зверніть увагу , що «об'єднання», ймовірно , не найкращий термін для A + B тут - ось конкретно непересічних об'єднання двох типів, так як обидві сторони відрізняються , навіть якщо їх типи збігаються. Для чого це варто, більш поширеним терміном є просто "тип суми".

  • Факти одинарного типу - це фактично всі типи одиниць. Вони поводяться однаково під алгебраїчними маніпуляціями і, що ще важливіше, кількість наявної інформації все ще зберігається.

  • Ви, мабуть, хочете і нульового типу. Haskell забезпечує це як Void. Немає значень, тип яких дорівнює нулю, так само як є одне значення, тип якого - один.

Тут ще відсутня одна велика операція, але я повернуся до неї за мить.

Як ви, напевно, помітили, Хаскелл схильний запозичувати поняття з Теорії категорій, і все вищезазначене має дуже просте тлумачення як таке:

  • З огляду на об'єкти A і B в Хаску , їхній продукт 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 :: Aіснує одне значення типу (A, B)для кожного жителя Росії 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: Що стосується конструкції "похідного типу", згаданої в інших відповідях, ви також можете насолоджуватися цим документом того самого автора, який надалі будує ідею, включаючи поняття поділу та інше цікаве.


3
Це велике пояснення, в зокрема , в якості плацдарму точки в такі речі , як strictlypositive.org/diff.pdf
acfoltzer

26
@acfoltzer: Дякую! :] І так, це чудовий папір, який розвиває ці ідеї. Ви знаєте, я думаю, що принаймні 5% моєї загальної репутації щодо ПЗ можна віднести до "допомоги людям зрозуміти одну з робіт Конора Макбріда" ...
CA McCann

45

Двійкові дерева визначаються рівнянням T=1+XT^2при семирізації типів. За побудовою T=(1-sqrt(1-4X))/(2X)визначається тим самим рівнянням при семіруванні складних чисел. Отже, враховуючи, що ми розв'язуємо одне і те ж рівняння в тому ж класі алгебраїчної структури, насправді не повинно дивуватися, що ми бачимо деякі подібності.

Проблема полягає в тому, що коли ми роздумуємо про поліноми при семіруванні складних чисел, ми зазвичай використовуємо той факт, що складні числа утворюють кільце або навіть поле, тому ми опиняємось за допомогою операцій, таких як віднімання, які не стосуються півколів. Але ми часто можемо усунути віднімання з наших аргументів, якщо у нас є правило, яке дозволяє скасувати з обох сторін рівняння. Це та річ, яку довели Фіоре та Ленстер , що показує, що багато аргументів про кільця можна перенести на півтони.

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

Однак є більше до цієї історії. Одна справа довести, що два типи рівні (скажімо), показавши два силові ряди рівні. Але ви також можете вивести інформацію про типи, ознайомившись із умовами в серії потужностей. Я не впевнений, якою має бути формальна заява тут. (Рекомендую Brent Йорг в папір на комбінаторних видах для деякої роботи , яка тісно пов'язана , але види НЕ такі ж , як типи.)

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

Весело!


Ця диференціація / один отвір контексту речі досить класні. Подивимось, чи маю я це прямо. Пара з алгебраїчним поданням P = X^2має похідне dP = X + X, таким Eitherє і однопрохідний контекст пари. Це досить круто. Ми могли б «інтегруватися», Eitherщоб отримати і пару. Але якщо ми спробуємо «інтегрувати» Maybe(з типом M = 1 + X), тоді нам потрібно мати \int M = X + X^2 / 2неглузде (що є половинним типом?) Це означає, що Maybeце не єдиний контекст жодного іншого типу?
Кріс Тейлор

6
@ChrisTaylor: Контексти з одним отвором зберігають інформацію про положення всередині продуктів, тобто, (A,A)з отвором у ньому - це Aтрохи і говорить вам, на якій стороні знаходиться отвір. AУ поодинці не має відмінне отвори для заповнення, тому ви не можете «інтегрувати» його. Звичайно, тип відсутньої інформації в цьому випадку є 2.
CA McCann

Я писав про розуміння таких типів, як X^2/2 blog.sigfpe.com/2007/09/type-of-distinct-pairs.html
sigfpe

@ user207442, ви також не робили щось про біекцію між одним деревом та семи деревами? У своїй відповіді я пов’язав із документом про це, але можу присягнути, що пам’ятаю, що вперше прочитав про це у своєму блозі.
CA McCann

1
@ChrisTaylor На кінцевому ( на насправді «розділити») відмінності є це: strictlypositive.org/CJ.pdf Але в той момент Конор не розуміли , що він описував відмінність. Я написав це, хоча це може бути складно дотримуватися: blog.sigfpe.com/2010/08/… Я написав би документ, але я не дуже добре їх закінчую.
sigfpe

22

Здається, що все, що ви робите, - це розширення відношення рецидивів.

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 + ...

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


1
"Оскільки правила для операцій над типами працюють як правила для арифметичних операцій ..." - вони не мають. Немає поняття віднімання типів, не кажучи вже про ділення і квадратних коренів. Тому я думаю, що моє запитання таке: коли ви можете перейти від алгебраїчної маніпуляції, якщо припустити X, що це елемент дійсних чисел до істинного твердження про типи, і, крім того, де відповідає відповідність (коефіцієнт nтерміна ступеня) <=> (число типів, що містять nелементи) походять?
Кріс Тейлор

1
Наприклад, з виразу для дерева ( T = 1 + T^2) я можу отримати T^6 = 1(тобто рішення - x^2 - x + 1 = 0це шосте коріння єдності), але явно не вірно, що тип продукту, що складається з шести двійкових дерев, еквівалент одиниці ().
Кріс Тейлор

3
@ChrisTaylor, але є що - то там відбувається, так як є ізоморфізм між T^7і T. пор. arxiv.org/abs/math/9405205
luqui

7
@ChrisTaylor, ось про що подумати. Додаючи нові алгебраїчні операції, ви сподіваєтесь не порушити властивості існуючих. Якщо ви можете дійти до однієї і тієї ж відповіді двома різними способами, вони повинні погодитися. Таким чином, забезпечення є будь-яке представлення взагалі для L = 1 + X * L, це було б краще бути таким же, що ви отримуєте , коли ви серії розширюватися, по консистенції. В іншому випадку ви можете запустити результат назад, щоб отримати щось неправдиве щодо результатів.
luqui

2
@ChrisTaylor Дійсно існує поняття поділу типів, шукайте "Типи коефіцієнта" для отримання додаткової інформації. Чи добре воно відповідає поліноміальному поділу, я не знаю. Це трапляється досить непрактично, імхо, але воно там.
Doug McClean

18

У мене немає повної відповіді, але ці маніпуляції, як правило, "просто працюють". Відповідним документом можуть бути об'єкти категорій, як складні номери Фіоре та Ленстера - я натрапив на цю книгу, читаючи блог sigfpe на відповідну тему ; решта цього блогу є золотим копальником для подібних ідей і його варто перевірити!

Ви також можете диференціювати типи даних, до речі - це отримає вам відповідну блискавку для типу даних!


12
Трюк блискавки є дивним. Я б хотів, щоб я це зрозумів.
шприц

Ви також можете робити блискавки в Scheme, використовуючи обмежені продовження, що дозволяє отримувати їх загально.
Джон Перді

10

Алгебра процесів комунікації (ACP) займається аналогічними видами виразів для процесів. Він пропонує додавання та множення як операторів для вибору та послідовності, з пов'язаними нейтральними елементами. На основі них існують оператори для інших конструкцій, такі як паралелізм та зрив. Дивіться http://en.wikipedia.org/wiki/Algebra_of_Communicating_Process . Також в Інтернеті є папір під назвою "Коротка історія алгебри процесів".

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

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

Сумка = А; (Сумка & a)

де А і підставка для вхідних і вихідних дій; крапка з комою та ампер і означають послідовність та паралельність. Дивіться відео на SkillsMatter, доступне за попереднім посиланням.

Специфікація сумки порівнянна з

L = 1 + X • L

було б

B = 1 + X&B

ACP визначає паралелізм з точки зору вибору та послідовності за допомогою аксіом; дивіться статтю у Вікіпедії. Цікаво, якою була б аналогія сумки

L = 1 / (1-X)

Програмування стилю ACP зручно для текстових аналізаторів та контролерів графічного інтерфейсу. Технічні характеристики, як

searchCommand = натиснуто (searchButton) + клавіша (Enter)

cancelCommand = натиснуто (відмінити кнопку) + клавіша (Escape)

можна записати більш стисло, зробивши два уточнення "клацанням" та "клавішею" неявними (як те, що Scala дозволяє за допомогою функцій). Отже, ми можемо написати:

searchCommand = searchButton + Enter

cancelCommand = cancelButton + Escape

Права сторона тепер містить операнди, які є даними, а не процесами. На цьому рівні не потрібно знати, які неявні уточнення перетворять ці операнди в процеси; вони не обов'язково уточнюватимуть вхідні дії; Вихідні дії також застосовуватимуться, наприклад, у специфікації тестового робота.

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


6

Вирахування та серія Маклаурінів з типами

Ось ще одне незначне доповнення - комбінаторне розуміння того, чому коефіцієнти розширення рядів повинні «працювати», зокрема зосереджуючись на рядах, які можна отримати з використанням підходу Тейлора-Маклауріна з обчислення. Примітка: прикладом розширення серії маніпульованих типів списку є серія Maclaurin.

Оскільки інші відповіді та зауваження стосуються поведінки виразів алгебраїчного типу (суми, продукти та показники), ця відповідь ухилиться від цієї деталі та зосередиться на типі "обчислення".

Ви можете помітити перевернуті коми, які роблять важкий підйом у цій відповіді. Є дві причини:

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

Визначення серії Маклауріна

Серія 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'

що може бути кращим.

Зверніть увагу, що рівності можна зробити суворими та точними, якщо ми визначимо похідні, використовуючи класи ізоморфізму типів та функторів.

Тепер ми, зокрема, зауважимо, що правила обчислення, що стосуються алгебраїчних операцій додавання, множення та складу (часто їх називають правилами Сума, Добуток і Ланцюг), відображаються саме операцією "створення дірки". Крім того, базові випадки "створення діри" в постійному виразі або в самому терміні xтакож ведуть себе як диференціювання, тому за допомогою індукції ми отримуємо поведінку, що нагадує диференціацію для всіх виразів алгебраїчного типу.

Тепер ми можемо інтерпретувати диференціацію, що означає n"похідна" виразу типу dⁿe/dxⁿ? Це тип, що представляє n-місткі контексти: терміни, які при "заповненні" 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контексту-місця для терміна типу, f(x)який ще не містить x - тобто коли ми їх "інтегруємо" nразів , отриманий термін має рівно n x s, ні більше, ні менше. Тоді інтерпретація типу f⁽ⁿ⁾(0)як числа (як у коефіцієнтах серії Маклауріна f) є просто підрахунком кількості таких nконтекстів на порожньому місці. Ми майже там!

Але де це (1/n!)закінчується? Вивчення процесу типу "диференціації" показує нам, що при застосуванні декількох разів він зберігає "порядок", в якому витягуються підтерміни. Наприклад, розгляньте термін (x₀, x₁)типу x × xта операцію "проробляння отвору" в ньому двічі. Отримуємо обидві послідовності

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

незважаючи на те, що обидва походять з одного і того ж терміна, оскільки є 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

для отримання коефіцієнта в серії Маклауріна.

Але оскільки ми впевнені, що ці вирази справді суворо "диференційовані", якщо тільки неявно, і оскільки ми маємо відповідність функціям ℝ → ℝ, де похідні, безумовно, унікальні, ми можемо бути впевнені, що навіть якщо ми отримаємо значення за допомогою " незаконні операції, результат дійсний.

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

Що стосується вашого питання щодо складу функцій, я вважаю, що ланцюгове правило дає часткову відповідь.

Я не впевнений, на скільки ADT-стилів у стилі Haskell це стосується, але я підозрюю, що це багато, якщо не все. Я виявив справді чудовий доказ цього факту, але цей запас занадто малий, щоб його містити ...

Тепер, безумовно, це лише один із способів розібратися, що відбувається тут, і, мабуть, існує багато інших шляхів.

Короткий зміст: TL; DR

  • тип 'диференціація' відповідає ' робити дірку '.
  • застосовуючи функтор, щоб 0отримати для нас це "порожні" умови.
  • Таким чином, серія потужності Маклауріна (дещо) суворо відповідає перерахуванню кількості членів типу функтора з певною кількістю елементів.
  • неявна диференціація робить це більш водонепроникним.
  • унікальність похідних і унікальність силових рядів означають, що ми можемо підробити деталі, і це працює.

6

Теорія залежних типів і функції "довільного" типу

Моя перша відповідь на це запитання була високо понятною і мало детальною і відображена під питанням: "що відбувається?"; ця відповідь буде такою ж, але зосереджена на підзапиті, "чи можна отримати функції довільного типу?".

Одне розширення алгебраїчних операцій суми і твори є так звані «великі оператори», які представляють собою суму і твір послідовності (або в більш загальному плані , суми і добутку функції над областю) , як правило , написані Σі Πвідповідно. Див. Позначення Sigma .

Отже сума

a + aX + aX² + ...

може бути написано

Σ[i  ℕ]aX

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

Якщо дивитися здалеку, такий вираз виглядає як «довільна» функція X; ми, звичайно, обмежені виразними рядами та пов'язаними з ними аналітичними функціями. Це кандидат у представництво в теорії типів? Безумовно!

Клас теорій типів, що мають безпосереднє уявлення про ці вирази, - це клас теорій "залежних" типів: теорії із залежними типами. Природно, у нас є терміни, залежні від термінів, і в таких мовах, як Haskell, з функціями типу та кількісним визначенням типу, термінами та типами залежно від типів. У залежних умовах ми додатково маємо типи залежно від термінів. Haskell не є залежно типізованою мовою, хоча багато особливостей залежних типів можна імітувати , трохи тортуючи мову .

Каррі-Говард та залежні типи

«Ізоморфізм Крірі-Говарда» розпочав життя як спостереження про те, що терміни та правила судження про просто набране лямбда-числення точно відповідають природній дедукції (сформульованій Генцену), застосованій до інтуїтивної логіки пропозицій, причому типи займають місце пропозицій , а також терміни, що займають місце доказів, незважаючи на те, що два незалежно винайдені / виявлені З тих пір це величезне джерело натхнення для теоретиків типів. Однією з найбільш очевидних речей, на яку слід звернути увагу, є те, чи може, і як ця відповідність логіці пропозицій може бути розширена до логіки предикатів або вищого порядку Спочатку теорії залежних типів виникли з цього напряму розвідки.

Про вступ до ізоморфізму Крірі-Говарда для просто набраного лямбдального числення дивіться тут . Наприклад, якщо ми хочемо довести, 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)|"вибір".

Отже,

|∃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

щоб ми могли застосувати довільні коефіцієнти до ряду потужностей.

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

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

Існує кілька способів представлення типів на рівні терміна. Я буду використовувати тут уявну хаскельську позначення з *для Всесвіту типів, що зазвичай вважається типом у залежній обстановці. 1

Аналогічно, існує також щонайменше стільки ж способів позначити " -значення", скільки існує теорій залежного типу. Я буду використовувати позначення відповідності шаблону 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

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

  1. Багато теорій вимагають, щоб члени *були просто представниками типів, і операція забезпечується як явне відображення від термінів типу *до їх асоційованих типів. Інші теорії дозволяють самим буквальним типам бути суб'єктами термінового рівня.

'Довільні' функції?

Тепер у нас є апарат для вираження цілком загального ряду потужностей як типу!

Серія

Σ[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може бути будь-що взагалі, з огляду на мову Тюрінга Повної із залежними типами, ми можемо представляти будь-яку аналітичну функцію з натуральними коефіцієнтами чисел.

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

Сподіваємось, ця відповідь певним чином вивчає, наскільки далеко ми можемо пройти функції довільного типу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.