Heterogene gesponserte Typen zurück zu Werten reflektieren, kompositorisch

8

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.

Hintergrund

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:

%Vor%

Es ist ein einfacher Rosenwald auf Typenniveau, der wie dieses sehr detaillierte Diagramm aussieht:

%Vor%

Lösungsversuch

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:

  • Ich benötige eine Art Look-Ahead, um das Ergebnis einer höherstufigen Reflexion für die generische Listenreflexion auf Typenebene zu kennen - PostReflection type function
  • Ich muss 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!

    
Athan Clark 19.01.2015, 17:15
quelle

1 Antwort

9

Die faule Lösung

Installieren Sie das Paket singletons :

%Vor%

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

%Vor%

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:

%Vor%

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.

Die Do-it-yourself-Lösung

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!).

%Vor%

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.

%Vor%

Die Reflexion ist jetzt ziemlich einfach:

%Vor%     
András Kovács 19.01.2015, 20:50
quelle