Ich habe kürzlich mit -XDataKinds
gespielt und würde gerne eine erweiterte Struktur mit Typfamilien erstellen und auf die Wertebene zurückziehen. Ich glaube, dass dies möglich ist, weil die kompositorischen Komponenten sehr einfach sind und die terminalen Ausdrücke genauso einfach sind.
Ich möchte einfache Rosenbäume von Strings
abstufen / reflektieren, die zu types of% Tree Symbol
werden (wenn GHC.TypeLits.Symbol
als String auf Textebene verwendet wird). Hier ist mein Standardcode:
Es ist ein einfacher Rosenwald auf Typenniveau, der wie dieses sehr detaillierte Diagramm aussieht:
%Vor% Idealerweise würde ich diese Struktur durchqueren und eine 1-zu-1-Zuordnung zu -Werten vom Typ *
zurückgeben, aber es ist nicht sehr offensichtlich, wie dies heterogen durchgeführt wird, während es noch ausgeführt wird über die (notwendigen) Instanzen wegen Überlastung.
vanila auf #haskell
vorgeschlagen Ich verwende Typklassen, um die zwei Welten zu verbinden, aber es scheint ein bisschen schwieriger als ich dachte. Bei meinem ersten Versuch habe ich versucht, den Inhalt einer Musterübereinstimmung auf Typenebene über eine Instanzkopfeinschränkung zu kodieren, aber mein zugehöriger Typ (um das Ergebnis *
-kinded des Mappings zu codieren) überlappte sich - anscheinend Instanzköpfe werden von GHC etwas ignoriert .
Idealerweise möchte ich auch, dass die Reflexion von Listen und Bäumen generisch ist, was scheinbar Probleme verursacht - es ist wie die Verwendung von Typklassen, um die Art / Art-Schichten zu organisieren.
Hier ist ein nicht funktionelles Beispiel von dem, was ich möchte:
%Vor%...
Es gibt ein paar Dinge, die an diesem Code generell falsch sind. Hier ist, was ich sehe:
PostReflection
type function Proxy
im laufenden Betrieb erstellen und zerstören. Ich bin mir nicht sicher, ob das momentan kompiliert wird, aber ich bin nicht zuversichtlich, dass die Typen sich so vereinheitlichen, wie ich es von ihnen erwarte. Aber diese Klassenhierarchie scheint die einzige Möglichkeit zu sein, eine heterogene Grammatik zu erfassen, also könnte das noch ein Anfang sein. Jede Hilfe dabei wäre enorm!
Installieren Sie das Paket singletons
:
Und wir sind fertig.
Leider ist die Bibliothek spärlich dokumentiert und ziemlich komplex. Ich empfehle, die Projekthomepage für weitere Dokumentationen zu konsultieren. Ich versuche, die Grundlagen unten zu erklären.
Sing
ist die Datenfamilie, die die Singleton-Repräsentationen definiert. Singletons sind strukturell die gleichen wie die unlifted Typen, aber ihre Werte werden durch die entsprechenden angehobenen Werte indiziert. Zum Beispiel wäre der Singleton von data Nat = Z | S Nat
singletons
ist die Template-Funktion, die die Singletons erzeugt (und hebt auch die abgeleiteten Instanzen auf und kann auch Funktionen heben).
SingKind
ist im Wesentlichen eine Art-Klasse , die uns den Demote
-Typ und fromSing
zur Verfügung stellt. Demote
gibt uns den entsprechenden nicht-abgehobenen Typ für einen aufgehobenen Wert. Beispiel: Demote False
ist Bool
, während Demote "foo"
Symbol
ist. fromSing
konvertiert einen Singleton-Wert in den entsprechenden nicht angehobenen Wert. Also fromSing SZ
ist gleich Z
.
SingI
ist eine Klasse, die die angehobenen Werte in Singleton-Werte wiedergibt. sing
ist ihre Methode und sing :: Sing x
gibt uns den Singleton-Wert von x
. Das ist fast das, was wir wollen; Um die Definition von reflect
zu vervollständigen, müssen wir nur fromSing
auf sing
verwenden, um den unbelasteten Wert zu erhalten.
KProxy
ist ein Export von Data.Proxy
. Es ermöglicht uns, Artvariablen aus der Umgebung zu erfassen und sie in Definitionen zu verwenden. Beachten Sie, dass jeder beliebige promotierende Datentyp (* - & gt; *) anstelle von KProxy
verwendet werden kann. Details zur Dateityp-Promotion finden Sie hier.
Beachten Sie jedoch, dass es bei Arten eine schwächere Form des Versands gibt, die nicht KProxy
erfordert:
So weit, so gut, aber wie schreiben wir die Instanz für gehobene Listen?
%Vor% Demote a
ist natürlich nicht erlaubt, weil a
eine Art ist, kein Typ. Also brauchen wir KProxy
, um a
auf der rechten Seite nutzen zu können.
Dies geht ähnlich wie bei der singletons
-Lösung, aber wir überspringen bewusst die Singleton-Darstellungen und gehen direkt zur Reflexion. Das sollte etwas performanter sein, und wir könnten sogar ein bisschen lernen (das habe ich sicherlich getan!).
Wir implementieren die Art Versand als Open-Type-Familie und bieten ein Typen-Synonym für Convenience:
%Vor% Das allgemeine Muster ist, dass wir 'KProxy :: KProxy k
verwenden, wenn wir eine Art k
erwähnen wollen.
Die Reflexion ist jetzt ziemlich einfach:
%Vor%Tags und Links haskell reflection dependent-type typeclass data-kinds