„Chronione” negatywne zdarzenia w definicji typów indukcyjnych, zawsze złe?


11

Wiem, jak niektóre negatywne zdarzenia mogą być zdecydowanie złe:

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

Nie jestem jednak pewien, czy:

  • wszystkie typy indukcyjne z negatywnymi zdarzeniami mogą okazać się błędne;

  • jeśli tak, istnieje znany mechaniczny sposób;

Na przykład walczyłem, próbując sprawić, że ten typ się nie powiedzie:

type Not a = a -> False

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

Doceniamy każdy wskaźnik do literatury na ten temat.


1
Czy to jest Coq? Haskell? Teoria pseudo-typu? Co rozumiesz przez „pójść nie tak”?
Dave Clarke

@DaveClarke Przepraszamy, kod to Haskell, ale bardziej dotyczy języków takich jak Coq lub Agda, w których zabronione są negatywne zdarzenia. Przez „pójść nie tak” rozumiem możliwość napisania rozbieżnego terminu, a tym samym zamieszkiwania w False, tak jak w moim przykładzie w Haskell.
Ptival

Odpowiedzi:


10

Powód zakazu negatywnych zdarzeń można zrozumieć analogicznie do twierdzenia Knastera-Tarskiego. To twierdzenie tak mówi

jeśli jest kompletną siecią, a jest funkcją monotoniczną na , to zbiór stałych punktów jest również kompletną siecią. W szczególności istnieje najmniej stały punkt i największy stały punkt .f : L L L f μ f ν fLf:LLLfμfνf

W tradycyjnej teorii modeli sieci można postrzegać jako zdania, a relację rzędu można rozumieć jako uwikłanie (tj. Że prawdą jest prawdę ).p q q pLpqqp

Kiedy przechodzimy od teorii modelu do teorii dowodu, sieci uogólniają się na kategorie. Typy mogą być postrzegane jako obiekty o kategorii i mapy oznacza dowód , że może pochodzić z . e : P Q Q QCe:PQQQ

Kiedy próbujemy interpretować typy zdefiniowane przez równania rekurencyjne, ee, , oczywistą rzeczą do zrobienia jest poszukiwanie uogólnienia twierdzenia Knastera-Tarskiego. Więc zamiast funkcji monotonicznej na sieci wiemy, że chcemy funktora , który wysyła obiekty do obiektów, ale uogólnia warunek monotoniczności, tak że każda mapa otrzymuje mapę (z warunkami koherencji, że wysyła tożsamości do tożsamości i zachowuje kompozycje, tak że ).N=μα.1+α F:CCe:PQF(e):F(P)F(Q)FF(gf)=F(g)F(f)

Jeśli więc chcesz indukcyjnego typu danych , musisz także podać akcję funkcjonalną na warunkach dla operatora typu , aby mieć pewność, że żądany punkt istnieje. Surowy warunek dodatni w Agdzie i Coq jest warunkiem składniowym , który implikuje to ograniczenie semantyczne . Mówiąc luźniej, mówi, że jeśli zbudujesz operator typu na podstawie sum i produktów, zawsze możesz przygotować akcję funcjonalną, a zatem każdy typ utworzony w ten sposób powinien mieć ustalony punkt.μα.F(α)F

W językach o typie zależnym masz również typy indeksowane i parametryzowane, więc Twoje prawdziwe zadanie jest bardziej skomplikowane. Bob Atkey (który pisał o tym tutaj i tutaj ) mówi mi, że dobrym miejscem do znalezienia tej historii jest:

Jak zauważa Andrej, zasadniczo to, czy zdarzenie negatywne jest w porządku, czy nie, zależy od tego, jaki jest twój model teorii typów. Zasadniczo, kiedy masz definicję rekurencyjną, szukasz punktu stałego i istnieje wiele twierdzeń o punkcie stałym w matematyce.

Jednym z nich, z którego osobiście korzystałem, jest twierdzenie Banacha o punkcie stałym, które mówi, że jeśli masz ściśle skurczową funkcję w przestrzeni metrycznej, to ma ona unikalny punkt stały. Pomysł ten został wprowadzony do semantyki przez (IIRC) Maurice'a Nivata i był intensywnie badany przez Amerykę i Rutten, a niedawno został połączony przez Birkedala i jego współpracowników z popularną techniką operacyjną zwaną „indeksowaniem kroków”.

Daje to początek teoriom typów, w których dozwolone są negatywne zdarzenia w typach rekurencyjnych, ale tylko wtedy, gdy negatywne zdarzenia występują pod specjalnym konstruktorem typu „ochrona”. Pomysł ten wprowadził Hiroshi Nakano, a związek z twierdzeniem Banacha został zrobiony zarówno przeze mnie, jak i przez Nicka Bentona, a także przez Larsa Birkedala i jego współautorów.


7

Czasami możesz rozwiązać rekurencyjne równania „na szczęście”.

Zakładam, że chcesz to zrobić w zestawach (w przeciwieństwie do jakiejś teorii domen) Jeśli rozwiniemy twoją definicję i zapiszemy równanie bezpośrednio bez adnotacji Haskella, otrzymamy Rozważmy dwa przypadki:

A(A)A.
  1. Jeśli jest zamieszkały, tzn. Zawiera coś, to , dlatego równanie zmniejsza się do I rzeczywiście, singleton set rozwiązuje równanie.AA

    AA1.
    1
  2. Jeśli jest puste, otrzymujemy .A()1

Wniosek: istnieją dwa rozwiązania, pusty typ (który nazwałeś False) i typ urządzenia ().

Oto kolejny interesujący przykład: lub w Haskell

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

Pod względem zestawów jest to . Według twierdzenia Cantora nie ma rozwiązania, ponieważ ma mniej elementów niż . Jeśli jednak przyjrzymy się temu równaniu w przestrzeniach topologicznych, istnieje rozwiązanie, a mianowicie Dzieje się tak dlatego, że to przestrzeń Cantora, to przestrzeń jej podzbiorów clopen, których jest niezliczona liczba (i trzeba to zobaczyć tworzą dyskretną przestrzeń). Podobnie możesz wykazać bijection pomiędzy i w Haskell.A22AA22A

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

3

Trudno jest coś dodać do wyjaśnień Andreja lub Neela, ale dam temu szansę. Spróbuję zająć się syntaktycznym punktem widzenia, zamiast próbować odkryć leżącą u podstaw semantykę, ponieważ wyjaśnienie jest bardziej elementarne i udzielę bardziej prostej odpowiedzi na twoje pytanie.

Zamierzam pracować w prostym typie -calculus zamiast w bardziej złożonym systemie leżącym u podstaw Haskell. Uważam w szczególności, że obecność zmiennych typu może do pewnego stopnia dezorientować.λ

Najważniejsze odniesienie jest następujące:

Mendler, N. (1991). Typy indukcyjne i ograniczenia typów w rachunku lambda drugiego rzędu. Obawiam się, że nie znalazłem referencji online. Stwierdzenia i dowody można jednak znaleźć w rozprawie doktorskiej Naxa (wysoce zalecana lektura!).

Mendler wyjaśnia, że ​​pozytywność jest koniecznym i wystarczającym warunkiem zakończenia w przypadku nierekurencyjnych definicji przypadków (i strukturalnie malejących definicji rekurencyjnych). Stwierdza to za pomocą równania równania . Podaję prosty przykład, który jest uproszczeniem typu .Bad

Bad=BadA

Gdzie jest dowolnym typem. Mamy wtedyA

λx:Bad.x x:BadA

a więc

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

Mendler pokazuje, że można tego dokonać dla dowolnego typu gdzie jest typem z co najmniej jednym ujemnym wystąpieniem (mogą również występować pozytywne) . Podaje wyraźny termin, który nie kończy się dla danego (strony 39-40 jego pracy).

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

Oczywiście nie pracujesz z typami równymi zdefiniowanymi, ale z konstruktorami , tzn. Masz

data Bad = Pack (Bad -> A)

zamiast ścisłej równości. Jednak możesz to zdefiniować

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

co jest wystarczające do utrzymania tego wyniku:

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

Termin ten jest nadal dobrze wpisane typu .A


W drugim przykładzie sprawy są nieco trudniejsze, ponieważ masz coś podobnego do

Bad=BadA

gdzie jest powiązany , ale nie równy, z (w twoim przypadku są one równe i odpowiednio). Przyznaję, że nie mogłem zbudować prostego izomorfizmu między nimi. Ten sam problem występuje, jeśli go wymieniszB d B d B d ( N O T )BadBadBad aBad (Not a)

type Not a = a -> False

z

data Not a = Not a

Łatwo byłoby to rozwiązać, gdyby Haskell zezwolił na takie definicje typów:

type Acc = Not Acc

W takim przypadku można zbudować kombinator pętli w dokładnie taki sam sposób jak poprzednio. Podejrzewam, że możesz nosić podobną (ale bardziej złożoną) konstrukcję

data Acc = D (Not Acc)

Problem polega na tym, że zbudować izomorfizm

Bad Acc <-> Bad (Not Acc)

masz do czynienia z wariancją mieszaną.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.