it-swarm.com.de

Missbrauch der Algebra algebraischer Datentypen - warum funktioniert das?

Der Ausdruck "algebraisch" für algebraische Datentypen sieht für jemanden mit mathematischem Hintergrund sehr suggestiv aus. Lassen Sie mich versuchen zu erklären, was ich meine.

Nachdem Sie die Grundtypen definiert haben

  • Produkt
  • Union +
  • Singleton X
  • Einheit 1

und unter Verwendung der Kurzschreibweise für X•X und 2X für X+X und so weiter können wir dann algebraische Ausdrücke für z. verknüpfte Listen

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

und binäre Bäume:

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

Nun, mein erster Instinkt als Mathematiker ist es, mit diesen Ausdrücken verrückt zu werden und zu versuchen, nach L und T zu lösen. Ich könnte dies durch wiederholtes Ersetzen tun, aber es scheint viel einfacher, die Notation fürchterlich zu missbrauchen und so zu tun, als könnte ich sie nach Belieben neu anordnen. Zum Beispiel für eine verknüpfte Liste:

L = 1 + X • L

(1 - X) • L = 1

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

dabei habe ich die Potenzreihenerweiterung von 1 / (1 - X) auf völlig ungerechtfertigte Weise verwendet, um ein interessantes Ergebnis abzuleiten, nämlich, dass ein L -Typ entweder Nil ist oder enthält 1 Element, oder es enthält 2 Elemente, oder 3 usw.

Interessanter wird es, wenn wir das für binäre Bäume machen:

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

wieder mit der Potenzreihenerweiterung (fertig mit Wolfram Alpha ). Dies drückt die für mich nicht offensichtliche Tatsache aus, dass es nur einen Binärbaum mit 1 Element, 2 Binärbäume mit zwei Elementen (das zweite Element kann sich auf dem linken oder rechten Zweig befinden), 5 Binärbäume mit drei Elementen usw. Gibt .

Meine Frage ist also: Was mache ich hier? Diese Operationen erscheinen ungerechtfertigt (was genau ist die Quadratwurzel eines algebraischen Datentyps?), Führen jedoch zu vernünftigen Ergebnissen. Hat der Quotient aus zwei algebraischen Datentypen in der Informatik irgendeine Bedeutung oder handelt es sich nur um eine Notationstrickserei?

Und, vielleicht interessanter, ist es möglich, diese Ideen zu erweitern? Gibt es eine Theorie der Algebra der Typen, die zum Beispiel beliebige Funktionen auf Typen zulässt, oder erfordern Typen eine Potenzreihendarstellung? Wenn Sie eine Klasse von Funktionen definieren können, hat dann die Zusammensetzung von Funktionen irgendeine Bedeutung?

275
Chris Taylor

Haftungsausschluss: Vieles davon funktioniert nicht ganz richtig, wenn Sie für ⊥ verantwortlich sind, daher werde ich dies der Einfachheit halber offen ignorieren.

Ein paar erste Punkte:

  • Beachten Sie, dass "Vereinigung" hier wahrscheinlich nicht der beste Begriff für A + B ist - das ist speziell a disjunkt Vereinigung der beiden Typen, da sich die beiden Seiten unterscheiden auch wenn ihre Typen gleich sind. Was es wert ist, ist der üblichere Begriff einfach "Summentyp".

  • Singleton-Typen sind effektiv alle Einheitentypen. Sie verhalten sich bei algebraischen Manipulationen identisch und, was noch wichtiger ist, die Menge der vorhandenen Informationen bleibt erhalten.

  • Sie möchten wahrscheinlich auch einen Null-Typ. Es gibt keinen Standardnamen, aber der häufigste ist Void. Es gibt keine Werte, deren Typ Null ist, genauso wie es einen Wert gibt, dessen Typ Eins ist.

Hier fehlt noch eine große Operation, aber darauf komme ich gleich zurück.

Wie Sie wahrscheinlich bemerkt haben, leiht sich Haskell häufig Konzepte aus der Kategorietheorie aus, und alle oben genannten Begriffe sind als solche sehr einfach zu interpretieren:

  • Bei den Objekten A und B in Hask ist ihr Produkt A × B der eindeutige Typ (bis zu Isomorphismus), der zwei Projektionen erlaubt fst: A × B → A und snd: A × B → B, wobei Typ C und Funktionen gegeben sind f: C → A, g: C → B Sie können die Paarung definieren f &&& g: C → A × B so, dass fst ∘ (f &&& g) = f und ebenfalls für g. Parametrizität garantiert die universellen Eigenschaften automatisch und meine weniger subtile Auswahl von Namen sollte Ihnen die Idee geben. Das (&&&) Operator ist definiert in Control.Arrow, Apropos.

  • Das Doppelte des oben Genannten ist das Coprodukt A + B mit Injektionen inl: A → A + B und inr: B → A + B, sofern angegeben Typ C und Funktionen f: A → C, g: B → C, Sie können das Copairing definieren f ||| g: A + B → C so, dass die offensichtlichen Äquivalenzen gelten. Auch hier garantiert die Parametrizität die meisten heiklen Teile automatisch. In diesem Fall lauten die Standardinjektionen einfach Left und Right, und das Copairing ist die Funktion either.

Viele der Eigenschaften von Produkt- und Summentypen lassen sich aus dem Obigen ableiten. Beachten Sie, dass jeder Singleton-Typ ein Terminalobjekt von Hask und jeder leere Typ ein Anfangsobjekt ist.

Zurück zu der oben erwähnten fehlenden Operation, in einer kartesischen geschlossenen Kategorie haben Sie Exponentialobjekte , die Pfeilen der Kategorie entsprechen. Unsere Pfeile sind Funktionen, unsere Objekte sind Typen mit Art * und der Typ A -> B verhält sich tatsächlich wie BEIN im Kontext der algebraischen Manipulation von Typen. Wenn es nicht klar ist, warum dies gelten sollte, betrachten Sie den Typ Bool -> A. Mit nur zwei möglichen Eingaben ist eine Funktion dieses Typs isomorph zu zwei Werten des Typs A, d. H. (A, A). Zum Maybe Bool -> A Wir haben drei mögliche Eingaben und so weiter. Beachten Sie auch, dass wir die Identität C erhalten, wenn wir die obige Copairing-Definition umformulieren, um die algebraische Notation zu verwendenEIN × CB = CA + B.

Wie für warum das alles ist sinnvoll - und insbesondere, warum Ihre Verwendung der Potenzreihenerweiterung gerechtfertigt ist - beachten Sie, dass sich ein Großteil der obigen Angaben auf die "Bewohner" eines Typs bezieht (dh , eindeutige Werte mit diesem Typ), um das algebraische Verhalten zu demonstrieren. Um diese Perspektive zu verdeutlichen:

  • Der Produkttyp (A, B) repräsentiert jeweils einen Wert aus A und B, unabhängig voneinander. Also für jeden festen Wert a :: A, es gibt einen Wert vom Typ (A, B) für jeden Einwohner von B. Dies ist natürlich das kartesische Produkt, und die Einwohnerzahl des Produkttyps ist das Produkt der Einwohnerzahl der Faktoren.

  • Der Summentyp Either A B steht für einen Wert aus A oder B, wobei der linke und der rechte Zweig unterschieden werden. Wie bereits erwähnt, handelt es sich um eine disjunkte Vereinigung, und die Einwohnerzahl des Summentyps ist die Summe der Einwohnerzahl der Summanden.

  • Der exponentielle Typ B -> A repräsentiert eine Zuordnung von Werten vom Typ B zu Werten vom Typ A. Für jedes feste Argument b :: B kann ein beliebiger Wert von A zugewiesen werden; ein Wert vom Typ B -> A wählt eine solche Zuordnung für jede Eingabe aus, was einem Produkt von so vielen Kopien von A entspricht, wie B Einwohner hat, daher die Potenzierung.

Während es zunächst verlockend ist, Typen als Mengen zu behandeln, funktioniert dies in diesem Zusammenhang nicht sehr gut - wir haben eher eine disjunkte Vereinigung als die Standardvereinigung von Mengen, es gibt keine offensichtliche Interpretation von Schnittmengen oder vielen anderen Mengenoperationen, und wir Normalerweise ist es nicht wichtig, die Mitgliedschaft festzulegen (überlässt dies der Typprüfung).

Andererseits verbringen die obigen Konstruktionen viel Zeit damit, über Zählen Einwohner zu sprechen, und Zählen die möglichen Werte eines Typs sind hier ein nützliches Konzept. Das führt uns schnell zu enumerative combinatorics , und wenn Sie den verlinkten Wikipedia-Artikel lesen, werden Sie feststellen, dass eines der ersten Dinge darin besteht, "Paare" und "Gewerkschaften" in genau demselben Sinne wie zu definieren Produkt- und Summentypen werden mit Funktionen generieren erstellt, und dann werden dieselben Aktionen für "Sequenzen" ausgeführt, die mit den Listen von Haskell identisch sind.


Edit: Oh, und hier ist ein kurzer Bonus, von dem ich denke, dass er den Punkt auffallend demonstriert. Sie haben in einem Kommentar erwähnt, dass für einen Baumtyp T = 1 + T^2 Sie können die Identität ableiten T^6 = 1, was eindeutig falsch ist. Jedoch, T^7 = T does halten, und eine Verzweigung zwischen Bäumen und Sieben-Tupeln-Bäumen kann direkt konstruiert werden, vgl. Andreas Blasses "Sieben Bäume in einem" .

Edit × 2: Zum Thema "Ableitung einer Typenkonstruktion", das in anderen Antworten erwähnt wird, könnte Ihnen auch dieser Beitrag von derselbe Autor , der auf der Idee weiter aufbaut, einschließlich der Begriffe der Teilung und anderer interessanter Dinge.

135
C. A. McCann

Binäre Bäume werden durch die Gleichung T=1+XT^2 Im Semiring von Typen definiert. Konstruktionsbedingt wird T=(1-sqrt(1-4X))/(2X) durch die gleiche Gleichung beim Semiren komplexer Zahlen definiert. Angesichts der Tatsache, dass wir dieselbe Gleichung in derselben Klasse algebraischer Strukturen lösen, sollte es eigentlich nicht überraschen, dass wir einige Ähnlichkeiten feststellen.

Der Haken dabei ist, dass wir, wenn wir über Polynome beim Semiren komplexer Zahlen nachdenken, normalerweise die Tatsache verwenden, dass die komplexen Zahlen einen Ring oder sogar ein Feld bilden, sodass wir Operationen wie die Subtraktion verwenden, die nicht für Semiren gelten. Aber wir können Subtraktionen von unseren Argumenten oft eliminieren, wenn wir eine Regel haben, die es uns erlaubt, von beiden Seiten einer Gleichung abzubrechen. Dies beweist Fiore und Leinster , dass viele Argumente über Ringe auf semirings übertragen werden können.

Dies bedeutet, dass viele Ihrer mathematischen Kenntnisse über Ringe zuverlässig auf Typen übertragen werden können. Infolgedessen können einige Argumente mit komplexen Zahlen oder Potenzreihen (im Ring der formalen Potenzreihen) auf völlig rigorose Weise auf Typen übertragen werden.

Die Geschichte beinhaltet jedoch noch mehr. Es ist eine Sache zu beweisen, dass zwei Typen gleich sind, indem gezeigt wird, dass zwei Potenzreihen gleich sind. Sie können jedoch auch Informationen zu Typen ableiten, indem Sie die Begriffe in der Potenzreihe überprüfen. Ich bin mir nicht sicher, wie die formelle Aussage hier aussehen soll. (Ich empfehle Brent Yorgeys Papier on kombinatorische Arten für einige Arbeiten, die eng miteinander verwandt sind, aber Arten sind nicht wie Typen.)

Was ich absolut umwerfend finde, ist, dass das, was Sie entdeckt haben, auf den Kalkül ausgeweitet werden kann. Theoreme über die Analysis können auf das Semiring von Typen übertragen werden. Tatsächlich können sogar Argumente über endliche Differenzen übertragen werden, und Sie stellen fest, dass klassische Theoreme aus der numerischen Analyse typentheoretische Interpretationen haben.

Habe Spaß!

43
sigfpe

Alles, was Sie tun, scheint die Wiederholungsbeziehung zu erweitern.

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

Und da die Regeln für die Operationen auf den Typen wie die Regeln für arithmetische Operationen funktionieren, können Sie mit algebraischen Mitteln herausfinden, wie Sie die Wiederholungsrelation erweitern können (da dies nicht offensichtlich ist).

20
newacct

Ich habe keine vollständige Antwort, aber diese Manipulationen neigen dazu, einfach zu funktionieren. Ein relevantes Papier könnte sein: Objekte mit Kategorien als komplexe Zahlen von Fiore und Leinster - Ich bin beim Lesen auf dieses Dokument gestoßen Sigfpes Blog zu einem verwandten Thema ; Der Rest dieses Blogs ist eine Goldmine für ähnliche Ideen und einen Blick wert!

Sie können übrigens auch Datentypen unterscheiden - so erhalten Sie den passenden Zipper für den Datentyp!

18
yatima2975

Die Algebra of Communicating Processes (ACP) befasst sich mit ähnlichen Arten von Ausdrücken für Prozesse. Es bietet Addition und Multiplikation als Operatoren für Auswahl und Reihenfolge mit zugehörigen neutralen Elementen. Basierend auf diesen gibt es Operatoren für andere Konstrukte wie Parallelität und Unterbrechung. Siehe http://en.wikipedia.org/wiki/Algebra_of_Communicating_Processes . Es gibt auch eine Online-Veröffentlichung mit dem Titel "Eine kurze Geschichte der Prozessalgebra".

Ich arbeite daran, Programmiersprachen mit ACP zu erweitern. Letzten April präsentierte ich einen Forschungsbericht bei Scala Days 2012, verfügbar unter http://code.google.com/p/subscript/

Auf der Konferenz habe ich einen Debugger demonstriert, der eine parallele rekursive Spezifikation eines Beutels ausführt:

Tasche = A; (Tasche & a)

wobei A und A für Eingabe- und Ausgabeaktionen stehen; Das Semikolon und das Et-Zeichen stehen für Sequenz und Parallelität. Sehen Sie sich das Video bei SkillsMatter an, das über den vorherigen Link erreichbar ist.

Eine Taschenspezifikation, die eher mit vergleichbar ist

L = 1 + X • L

wäre

B = 1 + X & amp; B

ACP definiert Parallelität in Bezug auf Auswahl und Reihenfolge unter Verwendung von Axiomen; Siehe den Wikipedia-Artikel. Ich frage mich, wozu die Taschenanalogie gut wäre

L = 1/(1-X)

Die Programmierung im ACP-Stil ist praktisch für Textparser und GUI-Controller. Spezifikationen wie

searchCommand = angeklickt (searchButton) + Taste (Enter)

cancelCommand = geklickt (cancelButton) + Taste (Escape)

kann präziser geschrieben werden, indem die beiden Verfeinerungen "angeklickt" und "Taste" impliziert werden (wie es Scala mit Funktionen erlaubt). Daher können wir schreiben:

searchCommand = searchButton + Enter

cancelCommand = cancelButton + Escape

Die rechte Seite enthält nun Operanden, die Daten und keine Prozesse sind. Auf dieser Ebene ist es nicht erforderlich zu wissen, welche impliziten Verfeinerungen diese Operanden in Prozesse verwandeln. sie würden sich nicht notwendigerweise zu Eingabeaktionen weiterentwickeln; Ausgabeaktionen würden ebenfalls zutreffen, z. in der Spezifikation eines Testroboters.

Prozesse erhalten auf diese Weise Daten als Begleiter. so präge ich den Begriff "Itemalgebra".

10

Kalkül- und Maclaurin-Reihe mit Arten

Hier ist eine weitere kleine Ergänzung - eine kombinatorische Einsicht, warum die Koeffizienten in einer Reihenexpansion "funktionieren" sollten, insbesondere mit Blick auf Reihen, die mit dem Taylor-Maclaurin -Ansatz aus der Analysis abgeleitet werden können. NB: Die Beispielserienerweiterung, die Sie für den manipulierten Listentyp angeben, ist eine Maclaurin-Serie.

Da sich andere Antworten und Kommentare mit dem Verhalten algebraischer Typausdrücke (Summen, Produkte und Exponenten) befassen, wird diese Antwort diese Details aufheben und sich auf den Typ "Kalkül" konzentrieren.

In dieser Antwort werden Sie möglicherweise Anführungszeichen bemerken, die zu einem starken Heben führen. Es gibt zwei Gründe:

  • wir haben die Aufgabe, Entitäten aus einem anderen Bereich Interpretationen zu geben, und es erscheint angebracht, solche fremden Begriffe auf diese Weise abzugrenzen.
  • einige Begriffe können strenger formalisiert werden, aber die Form und die Ideen scheinen wichtiger (und benötigen weniger Platz zum Schreiben) als die Details.

Definition von Maclaurin-Serie

Die Maclaurin-Reihe einer Funktion f : ℝ → ℝ Ist definiert als

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

dabei bedeutet f⁽ⁿ⁾ die nte Ableitung von f.

Um die Maclaurin-Reihe als mit Typen interpretiert verstehen zu können, müssen wir verstehen, wie wir drei Dinge in einem Typenkontext interpretieren können:

  • ein (möglicherweise vielfaches) Derivat
  • anwenden einer Funktion auf 0
  • begriffe wie (1/n!)

und es stellt sich heraus, dass diese Konzepte aus der Analyse geeignete Gegenstücke in der Typenwelt haben.

Was meine ich mit einem "geeigneten Gegenüber"? Es sollte den Geschmack eines Isomorphismus haben - wenn wir die Wahrheit in beide Richtungen bewahren können, können in einem Zusammenhang ableitbare Tatsachen auf den anderen übertragen werden.

Kalkül mit Typen

Was bedeutet die Ableitung eines Typausdrucks? Es stellt sich heraus, dass es für eine große und gut erzogene ('differenzierbare') Klasse von Typausdrücken und Funktoren eine natürliche Operation gibt, die sich ähnlich genug verhält, um eine geeignete Interpretation zu sein!

Um die Pointe zu verderben, werden analog zur Differenzierung "Ein-Loch-Kontexte" erstellt. This ist ein ausgezeichneter Ort, um diesen speziellen Punkt weiter zu erläutern, aber das Grundkonzept eines Ein-Loch-Kontexts (da/dx) Besteht darin, dass es das Ergebnis des Extrahierens eines einzelnen Unterpunkts von a darstellt bestimmter Typ (x) aus einem Begriff (vom Typ a), wobei alle anderen Informationen beibehalten werden, einschließlich derjenigen, die zur Bestimmung der ursprünglichen Position des Unterelements erforderlich sind. Eine Möglichkeit, einen Ein-Loch-Kontext für eine Liste darzustellen, sind beispielsweise zwei Listen: eine für Elemente, die vor der extrahierten standen, und eine für Elemente, die nach der extrahierten standen.

Die Motivation, diese Operation mit Differenzierung zu identifizieren, ergibt sich aus den folgenden Beobachtungen. Wir schreiben da/dx, Um den Typ der Ein-Loch-Kontexte für Typ a mit Loch vom Typ x zu bezeichnen.

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

Hier stehen 1 Und 0 Für Typen mit genau einem bzw. genau null Einwohnern, und + Und × Stehen wie gewohnt für Summen- und Produkttypen. f und g werden verwendet, um Typfunktionen oder Typausdrucksbilder darzustellen, und [f(x)/a] bedeutet, dass f(x) für jedes a im vorhergehenden Ausdruck.

Dies kann in einem Punkt-freien Stil geschrieben werden, wobei f' Geschrieben wird, um die abgeleitete Funktion der Typfunktion f zu bezeichnen, also:

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

das kann vorzuziehen sein.

Hinweis: Die Gleichungen können streng und genau festgelegt werden, wenn Ableitungen mithilfe von Isomorphismusklassen von Typen und Funktoren definiert werden.

Nun stellen wir insbesondere fest, dass die Rechenregeln für die algebraischen Operationen Addition, Multiplikation und Komposition (oft als Summen-, Produkt- und Kettenregeln bezeichnet) genau durch die Operation von ' ein Loch machen '. Darüber hinaus verhalten sich die Basisfälle, in denen in einem konstanten Ausdruck ein Loch gemacht wird, oder der Begriff x selbst ebenfalls als Differenzierung, sodass wir durch Induktion ein differenzierungsähnliches Verhalten für alle algebraischen Typausdrücke erhalten.

Nun können wir die Differenzierung interpretieren, was bedeutet die nte 'Ableitung' eines Typausdrucks, dⁿe/dxⁿ? Es ist ein Typ, der n - Ortskontexte darstellt: Begriffe, die, wenn sie mit n Begriffen des Typs x 'gefüllt' werden, einen e ergeben. Es gibt eine weitere wichtige Beobachtung in Bezug auf '(1/n!)', Die später kommt.

Der unveränderliche Teil eines Typfunktors: Anwenden einer Funktion auf 0

Wir haben bereits eine Interpretation für 0 In der Typwelt: einen leeren Typ ohne Mitglieder. Was bedeutet es aus kombinatorischer Sicht, eine Typfunktion darauf anzuwenden? Konkreter ausgedrückt: Angenommen, f ist eine Typfunktion. Wie sieht f(0) aus? Nun, wir haben sicherlich keinen Zugriff auf irgendetwas vom Typ 0, Daher sind Konstruktionen von f(x), die ein x erfordern, nicht verfügbar. Was übrig bleibt, sind die Begriffe, auf die in ihrer Abwesenheit zugegriffen werden kann, und die wir den "invarianten" oder "konstanten" Teil des Typs nennen können.

Ein explizites Beispiel ist der Funktor Maybe, der algebraisch als x ↦ 1 + x Dargestellt werden kann. Wenn wir dies auf 0 Anwenden, erhalten wir 1 + 0 - es ist genau wie bei 1: Der einzig mögliche Wert ist der Wert von None. In ähnlicher Weise erhalten wir für eine Liste nur den Begriff, der der leeren Liste entspricht.

Wenn wir es zurückbringen und den Typ f(0) als Zahl interpretieren, kann man es sich als die Anzahl von wie vielen Begriffen von vorstellen Typ f(x) (für jedes x) kann ohne Zugriff auf ein x abgerufen werden, dh die Anzahl der "leeren" Begriffe.

Zusammenfügen: Vollständige Interpretation einer Maclaurin-Reihe

Ich fürchte, ich kann mir keine angemessene direkte Interpretation von (1/n!) Als Typ vorstellen.

Wenn wir jedoch den Typ f⁽ⁿ⁾(0) im Lichte der obigen Ausführungen betrachten, sehen wir, dass er als der Typ von n -Platzkontexten für einen Ausdruck vom Typ f(x) die noch kein x enthalten - dh wenn wir sie n mal 'integrieren' Der resultierende Term hat genau nxs, nicht mehr und nicht weniger. Dann ist die Interpretation des Typs f⁽ⁿ⁾(0) als Zahl (wie in den Koeffizienten der Maclaurin-Reihe von f) einfach eine Zählung der Anzahl solcher leeren n -Plätze Kontexte gibt es. Wir sind fast da!

Aber wo landet (1/n!)? Die Untersuchung des Prozesses der Typendifferenzierung zeigt, dass bei mehrmaliger Anwendung die Reihenfolge erhalten bleibt, in der die Subtermen extrahiert werden. Betrachten Sie zum Beispiel den Begriff (x₀, x₁) Vom Typ x × x Und den Vorgang, ein Loch darin zweimal zu machen. Wir bekommen beide Sequenzen

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

obwohl beide aus dem gleichen Begriff stammen, gibt es 2! = 2 Möglichkeiten, zwei Elemente aus zwei zu nehmen, wobei die Reihenfolge erhalten bleibt. Im Allgemeinen es gibt n! Möglichkeiten, n Elemente aus n zu nehmen. Um die Anzahl der Konfigurationen eines Funktortyps mit n Elementen zu ermitteln, müssen wir den Typ f⁽ⁿ⁾(0) zählen und durch n! Dividieren genau wie in den Koeffizienten der Maclaurin-Reihe.

Das Teilen durch n! Erweist sich also als einfach als sich selbst interpretierbar.

Abschließende Überlegungen: 'rekursive' Definitionen und Analysen

Zunächst einige Beobachtungen:

  • wenn eine Funktion f: ℝ → ℝ eine Ableitung hat, ist diese Ableitung eindeutig
  • in ähnlicher Weise hat eine Funktion f: ℝ → ℝ genau eine entsprechende Polynomreihe, wenn sie analytisch ist

Da wir die Kettenregel haben, können wir implizite Differenzierung verwenden, wenn wir Typableitungen als Isomorphismusklassen formalisieren. Implizite Differenzierung erfordert jedoch keine außerirdischen Manöver wie Subtraktion oder Division! Damit können wir rekursive Typdefinitionen analysieren. Um Ihr Listenbeispiel zu nehmen, haben wir

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

und dann können wir auswerten

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

um den Koeffizienten von in der Maclaurin-Reihe zu erhalten.

Da wir jedoch davon überzeugt sind, dass diese Ausdrücke tatsächlich, wenn auch nur implizit, streng "differenzierbar" sind und wir die Entsprechung zu Funktionen ℝ → ℝ haben, bei denen Ableitungen mit Sicherheit eindeutig sind, können wir sicher sein, dass wir die Werte auch dann erhalten, wenn wir illegale Operationen, das Ergebnis ist gültig.

Um nun die zweite Beobachtung zu verwenden, wissen wir auf Grund der Entsprechung (ist es ein Homomorphismus?) Mit den Funktionen use → we, dass, vorausgesetzt wir sind zufrieden, dass eine Funktion hat eine Maclaurin-Serie, wenn wir überhaupt eine Serie finden , können die oben beschriebenen Prinzipien angewendet werden, um sie rigoros zu machen.

Ich nehme an, dass Ihre Frage zur Zusammensetzung von Funktionen durch die Kettenregel teilweise beantwortet wird.

Ich bin nicht sicher, für wie viele Haskell-artige ADTs dies gilt, aber ich vermute, dass es viele, wenn nicht alle sind. Ich habe einen wirklich wunderbaren Beweis für diese Tatsache gefunden, aber dieser Rand ist zu klein, um ihn zu enthalten ...

Nun, dies ist sicherlich nur ein Weg, um herauszufinden, was hier vor sich geht, und es gibt wahrscheinlich viele andere Wege.

Zusammenfassung: TL; DR

  • typ 'Differenzierung' entspricht ' ein Loch machen '.
  • durch Anwenden eines Funktors auf 0 erhalten wir die "leeren" Begriffe für diesen Funktor.
  • Maclaurin Potenzreihen entsprechen daher (etwas) rigoros der Aufzählung der Anzahl der Mitglieder eines Funktortyps mit einer bestimmten Anzahl von Elementen.
  • implizite Differenzierung macht dies wasserdichter.
  • die Einzigartigkeit von Derivaten und die Einzigartigkeit von Potenzreihen bedeuten, dass wir die Details verfälschen können und es funktioniert.
6
Oly

Abhängige Typentheorie und 'willkürliche' Typfunktionen

Meine erste Antwort auf diese Frage enthielt viele Konzepte und wenige Details und spiegelte sich in der Unterfrage „Was ist los?“ Wider. Diese Antwort ist dieselbe, konzentriert sich jedoch auf die Unterfrage "Können wir beliebige Typfunktionen erhalten?".

Eine Erweiterung der algebraischen Operationen von Summe und Produkt sind die sogenannten "großen Operatoren", die die Summe und das Produkt einer Sequenz (oder allgemeiner die Summe und das Produkt einer Funktion über eine Domäne) darstellen, die normalerweise geschrieben werden Σ bzw. Π. Siehe Sigma Notation .

Also die Summe

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

könnte geschrieben werden

Σ[i ∈ ℕ]aᵢXⁱ

wobei a beispielsweise eine Folge von reellen Zahlen ist. Das Produkt würde ähnlich mit Π Anstelle von Σ Dargestellt.

Wenn Sie aus der Ferne schauen, ähnelt diese Art von Ausdruck einer 'willkürlichen' Funktion in X; wir beschränken uns natürlich auf ausdrückbare Reihen und die damit verbundenen analytischen Funktionen. Ist dies ein Kandidat für eine Darstellung in einer Typentheorie? Bestimmt!

Die Klasse der Typentheorien, die unmittelbare Darstellungen dieser Ausdrücke haben, ist die Klasse der "abhängigen" Typentheorien: Theorien mit abhängigen Typen. Natürlich gibt es Begriffe, die von Begriffen abhängig sind, und in Sprachen wie Haskell mit Typfunktionen und Typquantifizierung, Begriffe und Typen, die von Typen abhängen. In einer abhängigen Einstellung haben wir zusätzlich Typen, die von Begriffen abhängen. Haskell ist keine abhängig typisierte Sprache, obwohl viele Merkmale abhängiger Typen simuliert werden können indem man die Sprache ein wenig quält .

Curry-Howard und abhängige Typen

Der "Curry-Howard-Isomorphismus" begann als Beobachtung, dass die Begriffe und Typbeurteilungsregeln der einfach typisierten Lambda-Rechnung genau der natürlichen Deduktion (wie von Gentzen formuliert) entsprechen, die auf die intuitionistische Aussagenlogik angewendet wird, wobei die Typen die Aussagen ersetzen und Begriffe, die den Platz von Beweisen einnehmen, obwohl die beiden unabhängig voneinander erfunden/entdeckt wurden. Seitdem ist es eine große Inspirationsquelle für Typentheoretiker. Eine der naheliegendsten Überlegungen ist, ob und wie diese Entsprechung für Aussagenlogik auf Prädikaten oder Logiken höherer Ordnung ausgedehnt werden kann. Aus dieser Forschungsrichtung gingen zunächst abhängige Typentheorien hervor.

Eine Einführung in den Curry-Howard-Isomorphismus für einfach typisierten Lambda-Kalkül finden Sie unter hier . Wenn wir zum Beispiel A ∧ B Beweisen wollen, müssen wir A und B beweisen; Ein kombinierter Beweis ist einfach ein Paar von Beweisen: einer für jede Konjunktion.

In natürlichem Abzug:

Γ ⊢ A    Γ ⊢ B
Γ ⊢ A ∧ B

und in einfach geschriebener Lambda-Rechnung:

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

Ähnliche Übereinstimmungen bestehen für - und Summentypen, - und Funktionstypen sowie die verschiedenen Eliminierungsregeln.

Ein unbeweisbarer (intuitionistisch falscher) Satz entspricht einem unbewohnten Typ.

In Anbetracht der Analogie der Typen als logische Sätze können wir überlegen, wie man Prädikate in der Typwelt modelliert. Es gibt viele Arten, wie dies formalisiert wurde (siehe diese Einleitung zu Martin-Löfs Intuitionistischer Typentheorie für einen weit verbreiteten Standard), aber der abstrakte Ansatz stellt gewöhnlich fest, dass ein Prädikat wie ein Satz mit Frei ist Termvariablen oder alternativ eine Funktion, die Terme zu Sätzen nimmt. Wenn wir zulassen, dass Typausdrücke Terme enthalten, bietet sich eine Behandlung im Lambda-Kalkül-Stil sofort als Möglichkeit an!

Was ist ein Beweis für ∀x ∈ X.P(x), wenn man nur konstruktive Beweise betrachtet? Wir können es uns als Beweisfunktion vorstellen, die Terme (x) zu Beweisen ihrer entsprechenden Sätze (P(x)) nimmt. Also sind Member (Beweise) des Typs (Satz) ∀x : X.P(x) 'abhängige Funktionen', die für jedes x in X einen Ausdruck des Typs P(x).

Was ist mit ∃x ∈ X.P(x)? Wir brauchen ein Mitglied von X, x zusammen mit einem Beweis von P(x). Mitglieder (Beweise) vom Typ (Satz) ∃x : X.P(x) sind also 'abhängige Paare': ein definierter Begriff x in X zusammen mit einem Begriff vom Typ P(x).

Notation: Ich werde verwenden

∀x ∈ X...

für aktuelle Aussagen zu Mitgliedern der Klasse X und

∀x : X...

für Typausdrücke, die einer universellen Quantifizierung über Typ X entsprechen. Ebenso für .

Kombinatorische Überlegungen: Produkte und Summen

Neben der Curry-Howard-Entsprechung von Typen mit Aussagen haben wir die kombinatorische Entsprechung von algebraischen Typen mit Zahlen und Funktionen, die der Hauptpunkt dieser Frage ist. Glücklicherweise kann dies auf die oben beschriebenen abhängigen Typen ausgeweitet werden!

Ich werde die Modulnotation verwenden

|A|

um die 'Größe' eines Typs darzustellen A, um die in der Frage umrissene Entsprechung zwischen Typen und Zahlen deutlich zu machen. Beachten Sie, dass dies ein Konzept ist, das außerhalb der Theorie liegt. Ich behaupte nicht, dass es einen solchen Operator in der Sprache geben muss.

Zählen wir die möglichen (vollständig reduzierten, kanonischen) Mitglieder des Typs

∀x : X.P(x)

dies ist der Typ von abhängigen Funktionen, die Terme x vom Typ X zu Terme vom Typ P(x) nehmen. Jede solche Funktion muss eine Ausgabe für jeden Term von X haben, und diese Ausgabe muss von einem bestimmten Typ sein. Für jedes x in X ergibt dies |P(x)| 'Auswahlmöglichkeiten' der Ausgabe.

Die Pointe ist

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

das macht natürlich keinen großen Sinn, wenn XIO () ist, ist aber auf algebraische Typen anwendbar.

Ebenso ein Begriff vom Typ

∃x : X.P(x)

ist die Art von Paaren (x, p) mit p : P(x), also können wir bei gegebenem x in X mit jedem Mitglied von P(x), wobei |P(x)| 'Entscheidungen' getroffen werden.

Daher,

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

mit den gleichen Vorbehalten.

Dies rechtfertigt die übliche Schreibweise für abhängige Typen in Theorien unter Verwendung der Symbole Π Und Σ, Und tatsächlich verwischen viele Theorien die Unterscheidung zwischen 'für alle' und 'Produkt' und zwischen 'es gibt' und "Summe", aufgrund der oben genannten Übereinstimmungen.

Wir kommen näher!

Vektoren: Darstellen abhängiger Tupel

Können wir nun numerische Ausdrücke wie

Σ[n ∈ ℕ]Xⁿ

als Typausdrücke?

Nicht ganz. Während wir informell die Bedeutung von Ausdrücken wie Xⁿ In Haskell betrachten können, wobei X ein Typ und n eine natürliche Zahl ist, handelt es sich um einen Missbrauch der Notation. Dies ist ein Typausdruck, der eine Zahl enthält: eindeutig nicht ein gültiger Ausdruck.

Andererseits ist bei abhängigen Typen im Bild ein Typ, der Zahlen enthält, genau der Punkt; In der Tat sind abhängige Tupel oder 'Vektoren' ein häufig genanntes Beispiel dafür, wie abhängige Typen pragmatische Sicherheit auf Typebene für Operationen wie den Listenzugriff bereitstellen können. Ein Vektor ist nur eine Liste mit Informationen auf Typebene bezüglich seiner Länge: genau das, was wir für Typausdrücke wie Xⁿ Brauchen.

Lassen Sie für die Dauer dieser Antwort

Vec X n

sei der Typ der Länge -n Vektoren von Werten vom Typ X.

Technisch gesehen ist n hier keine tatsächliche natürliche Zahl, sondern eine Darstellung einer natürlichen Zahl im System. Wir können natürliche Zahlen (Nat) im Peano-Stil entweder als Null (0) Oder als Nachfolger (S) einer anderen natürlichen Zahl darstellen, und für n ∈ ℕ Ich schreibe ˻n˼, Um den Ausdruck in Nat zu bezeichnen, der n darstellt. Zum Beispiel ist ˻3˼S (S (S 0)).

Dann haben wir

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

für jeden n ∈ ℕ.

Nat-Typen: Förderung von ℕ Begriffen zu Typen

Jetzt können wir Ausdrücke wie codieren

Σ[n ∈ ℕ]Xⁿ

als Typen. Dieser spezielle Ausdruck würde zu einem Typ führen, der natürlich isomorph zu dem Typ von Listen von X ist, wie in der Frage identifiziert. (Nicht nur das, sondern aus kategorietheoretischer Sicht ist die Typfunktion - die ein Funktor ist -, die X zu dem obigen Typ führt, natürlich isomorphic zum List functor.)

Ein letztes Puzzleteil für "beliebige" Funktionen ist das Codieren von z

f : ℕ → ℕ

ausdrücke wie

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

damit können wir beliebige Koeffizienten auf eine Potenzreihe anwenden.

Wir verstehen bereits die Korrespondenz von algebraischen Typen mit Zahlen, sodass wir von Typen auf Zahlen und von Typfunktionen auf numerische Funktionen abbilden können. Wir können auch den anderen Weg gehen! - Wenn man eine natürliche Zahl nimmt, gibt es offensichtlich einen definierbaren algebraischen Typ mit so vielen Gliedern, unabhängig davon, ob wir abhängige Typen haben oder nicht. Wir können dies außerhalb der Typentheorie leicht durch Induktion beweisen. Was wir brauchen, ist eine Möglichkeit, von natürlichen Zahlen auf Typen abzubilden, inside das System.

Eine erfreuliche Erkenntnis ist, dass, sobald wir abhängige Typen haben, Beweis durch Induktion und Konstruktion durch Rekursion sehr ähnlich werden - tatsächlich sind sie in vielen Theorien dasselbe. Da wir durch Induktion beweisen können, dass Typen existieren, die unsere Bedürfnisse erfüllen, sollten wir sie dann nicht konstruieren können?

Es gibt verschiedene Möglichkeiten, Typen auf Termebene darzustellen. Ich werde hier eine imaginäre Haskellish-Notation mit * Für das Universum der Typen verwenden, die selbst normalerweise als Typ in einer abhängigen Umgebung betrachtet wird.1

Ebenso gibt es mindestens so viele Möglichkeiten, ' - Elimination' zu notieren, wie es abhängige Typentheorien gibt. Ich werde eine Haskellish-Pattern-Matching-Notation verwenden.

Wir benötigen eine Zuordnung α Von Nat zu * Mit der Eigenschaft

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

Die folgende Pseudodefinition reicht aus.

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

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

Wir sehen also, dass die Aktion von α Das Verhalten des Nachfolgers S widerspiegelt und es zu einer Art Homomorphismus macht. Successor ist eine Typfunktion, die die Anzahl der Elemente eines Typs um eins erhöht. Das heißt, |Successor a| = 1 + |a| für jedes a mit einer definierten Größe.

Zum Beispiel α ˻4˼ (Das ist α (S (S (S (S 0))))), ist

Successor (Successor (Successor (Successor Zero)))

und die Begriffe dieser Art sind

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

geben Sie uns genau vier Elemente: |α ˻4˼| = 4.

Ebenso haben wir für jeden n ∈ ℕ

|α ˻n˼| = n

nach Bedarf.

  1. Viele Theorien verlangen, dass die Mitglieder von * Lediglich Repräsentanten von Typen sind, und eine Operation wird als explizite Zuordnung von Begriffen des Typs * Zu den ihnen zugeordneten Typen bereitgestellt. Andere Theorien erlauben, dass die Literaltypen selbst Entitäten auf Termebene sind.

Beliebige Funktionen?

Jetzt haben wir den Apparat, eine ganz allgemeine Potenzreihe als Typ auszudrücken!

Die Serie

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

wird der Typ

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

wobei ˻f˼ : Nat → Nat eine geeignete Darstellung innerhalb der Sprache der Funktion f ist. Wir können dies wie folgt sehen.

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

Wie 'willkürlich' ist das? Mit dieser Methode beschränken wir uns nicht nur auf ganzzahlige Koeffizienten, sondern auch auf natürliche Zahlen. Abgesehen davon kann f bei einer Turing Complete - Sprache mit abhängigen Typen alles sein, und wir können jede analytische Funktion mit natürlichen Zahlenkoeffizienten darstellen.

Ich habe die Wechselwirkung hiervon nicht untersucht, zum Beispiel mit dem Fall, der in der Frage von List X ≅ 1/(1 - X) angegeben ist, oder wie diese negativen und nicht ganzzahligen "Typen" in diesem Zusammenhang möglicherweise zu verstehen sind.

Hoffentlich hilft diese Antwort dabei, herauszufinden, wie weit wir mit beliebigen Typfunktionen gehen können.

5
Oly