«Охоронні» негативні явища у визначенні індуктивних типів, завжди погані?


11

Я знаю, як деякі негативні явища можуть бути остаточно поганими:

data False

data Bad a = C (Bad a -> a)

selfApp :: Bad a -> a
selfApp (x@(C x')) = x' x

yc :: (a -> a) -> a
yc f = selfApp $ C (\x -> f (selfApp x))

false :: False
false = yc id

Однак я не впевнений, чи:

  • всі індуктивні типи з негативними випадками можуть вийти не так;

  • якщо так, то відомий механічний спосіб цього;

Наприклад, я боровся, намагаючись не допустити такого типу:

type Not a = a -> False

data Bad2 a = C2 (Bad2 (Not a) -> a)

Буде вдячний будь-який вказівник літератури на цю тему.


1
Це Кок? Haskell? Теорія псевдотипів? Що ви маєте на увазі під "піти не так"?
Дейв Кларк

@DaveClarke Вибачте, кодом є Haskell, але стурбованість стосується більше таких мов, як Coq або Agda, де негативні випадки заборонені. Під «помилкою» я маю на увазі можливість написати термін, який розходиться, таким чином, зможу населити Хибне, як я робив у своєму прикладі в Haskell.
Птиваль

Відповіді:


10

Причину заборони негативних явищ можна зрозуміти за аналогією з теоремою Кнастер-Тарскі. Ця теорема говорить про це

якщо - повна решітка, а - монотонна функція на , то безліч фіксованих точок також є повною решіткою. Зокрема, є найменша фіксована точка і найбільша фіксована точка .Lf:LLLfμfνf

У традиційній теорії моделі решітки можна розглядати як пропозиції, а відношення порядку можна розуміти як потягнення (тобто, що правда з правдою ).Lpqqp

Коли ми переходимо від теорії моделей до теорії доказів, решітки узагальнюються до категорій. Тип можна розглядати як об'єкти категорії , і відображення являє собою доказ того, що може бути отриманий з .Ce:PQQQ

Коли ми намагаємось інтерпретувати типи, визначені рекурсивними рівняннями, ee, , очевидно, що потрібно зробити, це шукати узагальнення теореми Кнастера-Тарського. Таким чином , замість монотонної функції на решітці, ми знаємо , хочемо функтор , який відправляє об'єкти до об'єктів, але узагальнює умова монотонності , так що кожне відображення отримує карту (з умовами узгодженості, що посилає тотожності до тотожностей і зберігає композиції так, що ).N=μα.1+α F:CCe:PQF(e):F(P)F(Q)FF(gf)=F(g)F(f)

Отже, якщо ви хочете індуктивний тип даних , вам також потрібно надати функціональну дію на умовах оператора типу , щоб переконатися, що потрібна фіксована точка існує. Умова суворої позитивності в Агді та Кок є синтаксичним умовою, що передбачає це смислове обмеження. Якщо мова йде про те, що якщо ви будуєте оператор типу з сум і продуктів, то ви завжди можете готувати функціональну дію, і тому будь-який тип, сформований таким чином, повинен мати фіксовану точку.μα.F(α)F

У мовах залежно введених типів ви також маєте індексовані та параметризовані типи, тому ваше справжнє завдання складніше. Боб Еткі (який веде блог про це тут і тут ) каже мені, що гарне місце для пошуку історії:

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

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

Це породжує теорії типів, де дозволені негативні випадки в рекурсивних типах, але лише тоді, коли негативні випадки трапляються під спеціальним конструктором типу "захищеність". Цю ідею вніс Хіросі Накано, і зв’язок з теоремою Банаха зробив як я, так і Нік Бентон, а також Ларс Біркедал та його співавтори.


7

Іноді можна розв’язати рекурсивні рівняння «на удачу».

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

A(A)A.
  1. Якщо населений, тобто містить щось, то , тому рівняння зводиться до І дійсно, однорядне множина вирішує рівняння.A A A 1. 1AA

    AA1.
    1
  2. Якщо порожній, то отримуємо .( ) 1 A()1

Висновок: є два рішення: порожній тип (який ви назвали False) та тип одиниці ().

Ось ще один цікавий приклад: або в Haskell

A(A2)2,
data Cow a = Moo ((a -> Bool) -> Bool)

З точки зору множин це . За теоремою Кантора немає рішення, оскільки має менше елементів, ніж . Однак якщо ми подивимось на це рівняння в топологічних просторах, є рішення, а саме Це так, тому що - простір Кантора, - простір його підмножин clopen, яких існує чимало (і вам потрібно думати, щоб побачити, що вони утворюють дискретний простір). Аналогічно ви можете демонструвати біекцію між і в Haskell. 2 2 Н2 2 Н . 2 НA22AA22A

N22N.
2N22NInteger(Integer -> Bool) -> Bool

3

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

Я буду працювати в просто набраному -calculus, а не в більш складній системі, що лежить в основі Haskell. Я вважаю, зокрема, що наявність змінних типів може певною мірою вас бентежити.λ

Найважливішим посиланням є наступне:

Мендлер, Н. (1991). Індуктивні типи та обмеження типу в обчисленні лямбда другого порядку. Боюсь, я не знайшов посилання в Інтернеті. Проте твердження та докази можна знайти в кандидатській дисертації Накса (настійно рекомендується читати!).

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

Bad=BadA

Де - будь-який тип. У нас тоді єA

λx:Bad.x x:BadA

і так

(λx:Bad.x x) (λx:Bad.x x):A

Мендлер показує, що це може бути здійснено для будь-якого типу де - тип, що має принаймні одне негативне явище (можуть бути і позитивні випадки) . Він дає чіткий термін, який не може закінчитися для даного (стор. 39-40 його тези).F ( X ) X F ( X )

Bad=F(Bad)
F(X)XF(X)

Звичайно, ви працюєте не з рівнями, визначеними в рівній формі, а з конструкторами , тобто у вас є

data Bad = Pack (Bad -> A)

а не сувора рівність. Однак ви можете визначитись

unpack :: Bad -> (Bad -> A)
unpack (Pack f) = f

що достатньо для того, щоб цей результат і надалі мав місце:

 (\x:Bad -> unpack x x) (Pack (\x:Bad -> unpack x x))

Цей термін ще типізований типу .A


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

Bad=BadA

де є пов'язані , але НЕ рівні, до (в вашому випадку вони рівні і відповідно). Я визнаю, що я не міг побудувати прямого ізоморфізму між ними. Ця ж проблема є, якщо ви замінитеB a d B a d a B a d ( N o t a)BadBadBad aBad (Not a)

type Not a = a -> False

з

data Not a = Not a

Це було б легко вирішити, якби Haskell дозволив такі визначення типу:

type Acc = Not Acc

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

data Acc = D (Not Acc)

Біда в тому, що побудувати ізоморфізм

Bad Acc <-> Bad (Not Acc)

вам доведеться мати справу зі змішаною дисперсією.

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