"Bewachte" negative Vorkommen bei der Definition von induktiven Typen, immer schlecht?


11

Ich weiß, wie einige negative Ereignisse definitiv schlecht sein können:

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

Ich bin mir jedoch nicht sicher, ob:

  • Alle induktiven Typen mit negativen Vorkommen können falsch werden.

  • wenn ja, gibt es einen bekannten mechanischen Weg, dies zu tun;

Zum Beispiel habe ich versucht, diesen Typ falsch zu machen:

type Not a = a -> False

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

Jeder Hinweis auf Literatur zu diesem Thema wäre willkommen.


1
Ist das Coq? Haskell? Pseudotyp-Theorie? Was meinst du mit "schief gehen"?
Dave Clarke

@ DaveClarke Entschuldigung, der Code ist Haskell, aber es geht mehr um Sprachen wie Coq oder Agda, in denen negative Vorkommen verboten sind. Mit "schief gehen" meine ich, einen Begriff schreiben zu können, der divergiert, und somit False bewohnen zu können, wie ich es in meinem Beispiel in Haskell getan habe.
Ptival

Antworten:


10

Der Grund für das Verbot negativer Vorkommen kann in Analogie zum Knaster-Tarski-Theorem verstanden werden. Dieser Satz sagt das

Wenn ein vollständiges Gitter ist und f : L L eine monotone Funktion auf L ist , dann ist die Menge der Fixpunkte von f auch ein vollständiges Gitter. Insbesondere gibt es einen kleinsten Fixpunkt μ f und einen größten Fixpunkt ν f .Lf:LLLfμfνf

In der traditionellen Modelltheorie können Gitter als Sätze angesehen werden, und die Ordnungsbeziehung p q kann als Folge verstanden werden (dh, dass die Wahrheit von q durch die Wahrheit von p verbunden ist ).Lpqqp

Wenn wir von der Modelltheorie zur Beweistheorie übergehen, verallgemeinern sich Gitter auf Kategorien. Typen können als Objekte einer Kategorie , und eine Karte e : P Q repräsentiert einen Beweis dafür, dass Q von Q abgeleitet werden kann .Ce:PQQQ

Wenn wir versuchen, durch rekursive Gleichungen definierte Typen zu interpretieren, ist ee, ist es naheliegend, nach einer Verallgemeinerung des Knaster-Tarski-Theorems zu suchen. Anstelle einer monotonen Funktion auf einem Gitter möchten wir also einenFunktor F : CC , der Objekte an Objekte sendet, aber die Monotoniebedingung verallgemeinert, so dass jede Karte e : P Q eine Karte F ( e ) : F erhält ( P ) F ( Q ) (mit den Kohärenzbedingungen, dass F Identitäten an Identitäten sendet und Kompositionen beibehält, so dass F.N=μα.1+α F:CCe:PQF(e):F(P)F(Q)F ).F(gf)=F(g)F(f)

Wenn Sie also einen induktiven Datentyp μ α möchten . müssen Sie auch eine Funktionsaktion zu Begriffen für den Typoperator F bereitstellen, um sicherzustellen, dass der gewünschte Fixpunkt vorhanden ist. Die strenge Positivitätsbedingung in Agda und Coq ist einesyntaktischeBedingung, die diesesemantischeEinschränkungimpliziert. Wenn Sie einen Typoperator aus Summen und Produkten erstellen, können Sie die Funktionsaktion immer zusammenstellen. Daher sollte jeder auf diese Weise gebildete Typ einen festen Punkt haben.μα.F(α)F

In Sprachen mit abhängiger Eingabe haben Sie auch indizierte und parametrisierte Typen, sodass Ihre eigentliche Aufgabe komplizierter ist. Bob Atkey (der hier und hier darüber gebloggt hat ) sagt mir, dass ein guter Ort, um nach der Geschichte zu suchen, ist:

Wie Andrej bemerkt, hängt es grundsätzlich davon ab, welches Modell der Typentheorie Sie verwenden, ob ein negatives Auftreten in Ordnung ist oder nicht. Wenn Sie eine rekursive Definition haben, suchen Sie grundsätzlich nach einem Fixpunkt, und in der Mathematik gibt es viele Fixpunktsätze.

Eines, von dem ich persönlich viel Gebrauch gemacht habe, ist Banachs Fixpunktsatz, der besagt, dass wenn Sie eine streng kontraktive Funktion auf einem metrischen Raum haben, er einen eindeutigen Fixpunkt hat. Diese Idee wurde von (IIRC) Maurice Nivat in die Semantik eingeführt, von Amerika und Rutten eingehend untersucht und kürzlich von Birkedal und seinen Mitarbeitern mit einer beliebten Operationstechnik namens "Step-Indexing" in Verbindung gebracht.

Dies führt zu Typentheorien, bei denen negative Vorkommen in rekursiven Typen zulässig sind, jedoch nur dann, wenn die negativen Vorkommen unter einem speziellen Konstruktor vom Typ "Schutz" auftreten. Diese Idee wurde von Hiroshi Nakano eingeführt, und die Verbindung zu Banachs Theorem wurde sowohl von mir und Nick Benton als auch von Lars Birkedal und seinen Mitautoren hergestellt.


7

Manchmal kann man rekursive Gleichungen "durch Glück" lösen.

A(A)A.
  1. AA

    AA1.
    1
  2. A()1

Fazit: Es gibt zwei Lösungen, den leeren Typ (den Sie aufgerufen haben False) und den Einheitentyp ().

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

A22AA22A

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

3

Es ist schwer, Andrejs oder Neels Erklärungen etwas hinzuzufügen, aber ich werde es versuchen. Ich werde versuchen, den syntaktischen Standpunkt anzusprechen, anstatt zu versuchen, die zugrunde liegende Semantik aufzudecken, da die Erklärung elementarer ist und ich eine einfachere Antwort auf Ihre Frage gebe.

λ

Die entscheidende Referenz ist die folgende:

Mendler, N. (1991). Induktive Typen und Typbeschränkungen im Lambda-Kalkül zweiter Ordnung. Ich fürchte, ich habe online keine Referenz gefunden. Die Aussagen und Beweise finden sich jedoch in Nax ' Dissertation (eine sehr empfehlenswerte Lektüre!).

Bad

Bad=BadA

A

λx:Bad.x x:BadA

und so

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

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

Natürlich arbeiten Sie nicht mit gleichwertig definierten Typen, sondern mit Konstruktoren , dh Sie haben

data Bad = Pack (Bad -> A)

eher als strenge Gleichheit. Sie können jedoch definieren

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

was ausreicht, damit dieses Ergebnis weiterhin gilt:

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

A


In Ihrem zweiten Beispiel sind die Dinge etwas kniffliger, da Sie etwas in der Art von haben

Bad=BadA

BadBadBad aBad (Not a)

type Not a = a -> False

mit

data Not a = Not a

Es wäre leicht zu lösen, wenn Haskell solche Typdefinitionen zulassen würde:

type Acc = Not Acc

In diesem Fall können Sie einen Schleifenkombinator genauso erstellen wie zuvor. Ich vermute, Sie können eine ähnliche (aber komplexere) Konstruktion mit tragen

data Acc = D (Not Acc)

Das Problem hierbei ist, einen Isomorphismus aufzubauen

Bad Acc <-> Bad (Not Acc)

Sie müssen mit gemischten Varianz umgehen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.