Beziehung zwischen TypeRep und "Type" GADT

8

In Verschrotten Sie Ihr Textbaustein reloaded , beschreiben die Autoren eine neue Präsentation von Scrap Your Boilerplate , die dem entsprechen soll Original.

Ein Unterschied besteht jedoch darin, dass sie eine endliche, geschlossene Menge von "Basis" -Typen annehmen, die mit einer GADT codiert sind %Vor%

Im ursprünglichen SYB wird type-safe cast verwendet, implementiert mit der Klasse Typeable .

Meine Fragen sind:

  • Was ist die Beziehung zwischen diesen beiden Ansätzen?
  • Warum wurde die GADT-Repräsentation für die Präsentation "SYB Reloaded" ausgewählt?
Roman Cheplyaka 01.03.2013, 14:46
quelle

2 Antworten

4

[Ich bin einer der Autoren des "SYB Reloaded" -Papiers.]

TL; DR Wir haben es wirklich nur benutzt, weil es uns schöner erschien. Der klassenbasierte Typeable -Ansatz ist praktischer. Die Spine -Ansicht kann mit der Typeable -Klasse kombiniert werden und hängt nicht vom Type GADT ab.

Das Papier gibt dies in seinen Schlussfolgerungen an:

  

Unsere Implementierung behandelt die zwei zentralen Bestandteile der generischen Programmierung anders als das ursprüngliche SYB-Papier: Wir verwenden überladene Funktionen mit   explizite Typargumente anstelle von überladenen Funktionen basierend auf einem Typensafe   cast 1 oder ein klassenbasiertes erweiterbares Schema [20]; und wir benutzen den expliziten Rücken   eher als ein kombinatorbasierter Ansatz. Beide Änderungen sind unabhängig   und wir haben Klarheit geschaffen: Wir denken, dass die Struktur des SYB-Ansatzes in unserer Umgebung und in den Beziehungen sichtbarer ist   zu PolyP und Generic Haskell werden klarer. Wir haben gezeigt, dass während der   Wirbelsäulenansicht ist in der Klasse der generischen Funktionen begrenzt, die geschrieben werden können, ist es   anwendbar auf eine sehr große Klasse von Datentypen, einschließlich GADTs.

     

Unser Ansatz kann nicht einfach als Bibliothek verwendet werden, weil die Codierung von   Überladene Funktionen mit expliziten Typargumenten erfordern die Erweiterbarkeit von   der Datentyp Type und Funktionen wie toSpine. Man kann jedoch Spine in die SYB-Bibliothek integrieren, während man immer noch die Techniken des SYB verwendet   Papiere zu überladenen Funktionen zu kodieren.

Die Auswahl einer GADT für die Typendarstellung wurde daher hauptsächlich aus Gründen der Übersichtlichkeit getroffen. Wie Don in seiner Antwort erklärt, gibt es einige offensichtliche Vorteile in dieser Darstellung, nämlich dass es statische Informationen darüber enthält, für welchen Typ eine Typ-Repräsentation ist und dass es uns erlaubt, Cast ohne weitere Magie und insbesondere ohne die Verwendung zu implementieren von unsafeCoerce . Typindizierte Funktionen können auch direkt implementiert werden, indem der Musterabgleich für den Typ verwendet wird und ohne auf verschiedene Kombinatoren wie mkQ oder extQ zurückzugreifen.

Fakt ist, dass ich (und ich denke, die Co-Autoren) die Typeable -Klasse einfach nicht sehr mag. (In der Tat bin ich immer noch nicht, obwohl es jetzt endlich etwas disziplinierter wird, da GHC das automatische Ableiten für Typeable hinzufügt, es polymorph macht und letztendlich die Möglichkeit, eigene Instanzen zu definieren, aufhebt. Außerdem war Typeable nicht ganz so etabliert und weithin bekannt, wie es vielleicht jetzt ist, so dass es ansprechend schien, es durch die Verwendung der GADT-Codierung zu "erklären". Und außerdem war dies die Zeit, als wir darüber nachdachten, ob wir Haskell offene Dateitypen hinzufügen sollten Die GADT ist geschlossen.

Also, um es zusammenzufassen: Wenn Sie tatsächlich dynamische Informationen nur für ein geschlossenes Universum benötigen, würde ich mich immer für die GADT entscheiden, da Sie Mustererkennung verwenden können, um typenindexierte Funktionen zu definieren, und Sie müssen sich nicht darauf verlassen on unsafeCoerce noch fortgeschrittene Compiler Magie. Wenn das Universum jedoch offen ist, was durchaus üblich ist, sicherlich für die generische Programmierung, dann könnte der GADT-Ansatz aufschlussreich sein, ist aber nicht praktisch und die Verwendung von Typeable ist der richtige Weg.

Wie wir jedoch auch in den Schlussfolgerungen des Papiers feststellen, ist die Wahl von Type über Typeable keine Voraussetzung für die andere Wahl, die wir treffen, nämlich die Spine -Ansicht zu verwenden, die Ich denke, es ist wichtiger und wirklich der Kern des Papiers.

Das Papier selbst zeigt (in Abschnitt 8) eine Variation, die von der "Verschrotten Sie Ihre Boilerplate mit Klasse" inspiriert wurde Papier, das stattdessen eine Spine -Ansicht mit einer Klassenbeschränkung verwendet. Aber wir können auch eine direktere Entwicklung machen, die ich im Folgenden zeige. Dazu verwenden wir Typeable von Data.Typeable , aber definieren unsere eigene Data -Klasse, die der Einfachheit halber nur die Methode toSpine enthält:

%Vor%

Der Spine -Datentyp verwendet jetzt die Data -Einschränkung:

%Vor%

Die Funktion fromSpine ist so trivial wie bei der anderen Darstellung:

%Vor%

Instanzen für Data sind für flache Typen wie Int :

trivial %Vor%

Und sie sind immer noch völlig unkompliziert für strukturierte Typen wie binäre Bäume:

%Vor%

Das Papier geht dann weiter und definiert verschiedene generische Funktionen wie mapQ . Diese Definitionen ändern sich kaum.Wir erhalten nur Klassenbeschränkungen für Data a => , wenn das Papier Funktionsargumente von Type a -> :

hat %Vor%

Übergeordnete Funktionen wie everything verlieren auch nur ihre expliziten Typargumente (und sehen dann genauso aus wie im ursprünglichen SYB):

%Vor%

Wenn ich nun, wie oben gesagt, eine generische Summenfunktion definieren möchte, die alle Int Vorkommen zusammenfasst, können wir kein Muster mehr zuordnen, sondern müssen auf mkQ zurückgreifen, aber mkQ wird rein in definiert Begriffe von Typeable und völlig unabhängig von Spine :

%Vor%

Und dann (wieder genau wie im ursprünglichen SYB):

%Vor%

Für etwas späteres in der Arbeit (z. B. Hinzufügen von Konstruktordaten) wird etwas mehr Arbeit benötigt, aber es kann alles getan werden. Die Verwendung von Spine hängt also wirklich nicht davon ab, Type zu verwenden.

    
kosmikus 01.03.2013, 17:30
quelle
4

Nun, offensichtlich ist die Typeable Verwendung offen - neue Varianten können nachträglich hinzugefügt werden, ohne die ursprünglichen Definitionen zu ändern.

Die wichtige Änderung besteht darin, dass TypeRep nicht typisiert ist. Das heißt, es gibt keine Verbindung zwischen dem Laufzeittyp TypeRep und dem statischen Typ, den es codiert. Mit dem GADT-Ansatz können wir das Mapping zwischen einem Typ a und seinem Type , gegeben durch den GADT Type a , kodieren.

Wir machen also deutlich, dass der Typenrep mit seinem Ursprungstyp statisch verknüpft ist, und können statisch typisierte dynamische Anwendungen schreiben (zum Beispiel) mit Type a als Beweis, dass wir eine Laufzeit a haben.

Im älteren TypeRep-Fall gibt es keinen solchen Beweis, und es kommt auf die Laufzeit-String-Gleichheit an, und ein Zwang und Hoffnung für die Besten durch fromDynamic .

Vergleichen Sie die Signaturen:

%Vor%

versus GADT-Stil:

%Vor%

Ich kann meinen Typenbeweis nicht vortäuschen, und ich kann das später bei der Rekonstruktion von Dingen verwenden, z. Nachschlagen der Typklasseninstanzen für a , wenn alles, was ich habe, ein Type a ist.

    
Don Stewart 01.03.2013 15:33
quelle