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:
[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:
Der Spine
-Datentyp verwendet jetzt die Data
-Einschränkung:
Die Funktion fromSpine
ist so trivial wie bei der anderen Darstellung:
Instanzen für Data
sind für flache Typen wie Int
:
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 ->
:
Übergeordnete Funktionen wie everything
verlieren auch nur ihre expliziten Typargumente (und sehen dann genauso aus wie im ursprünglichen SYB):
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
:
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.
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.
Tags und Links haskell scrap-your-boilerplate