Haskell: Wie schreibt man eine monadische variadische Funktion mit Parametern, die den monadischen Kontext verwenden?

8

Ich versuche eine variadische Funktion mit einem monadischen Rückgabetyp zu erstellen, dessen Parameter auch den monadischen Kontext erfordern. (Ich bin mir nicht sicher, wie ich diesen zweiten Punkt beschreiben soll: zB printf kann IO () zurückgeben, aber es ist anders, dass seine Parameter gleich behandelt werden, egal ob es IO () oder String ist.)

Im Grunde habe ich einen Datenkonstruktor, der, sagen wir mal, zwei Char -Parameter hat. Ich möchte stattdessen zwei pointer style ID Char -Argumente bereitstellen, die automatisch von einer einschließenden State -Monade über eine Typklasseninstanz dekodiert werden können. Also, anstatt get >>= \s -> foo1adic (Constructor (idGet s id1) (idGet s id2)) zu machen, möchte ich fooVariadic Constructor id1 id2 machen.

Was folgt ist, was ich bis jetzt habe, Literaten-Haskell-Stil für den Fall, dass jemand es kopieren und damit herumspielen möchte.

Zuerst die grundlegende Umgebung:

%Vor%

Einige Testdaten zur Vereinfachung:

%Vor%

Ich habe diese nicht-monadische Version, die einfach die Umgebung als ersten Parameter annimmt. Das funktioniert gut, aber es ist nicht das, wonach ich suche. Beispiele:

%Vor%

(Ich könnte funktionale Abhängigkeiten oder Typfamilien verwenden, um die :: Foo type-Annotationen überflüssig zu machen. Im Moment bin ich nicht nervös deswegen, da es mich sowieso nicht interessiert.)

%Vor%

Nun möchte ich eine variadic Funktion, die in der folgenden Monade läuft.

%Vor%

Und im Grunde habe ich keine Ahnung, wie ich vorgehen soll. Ich habe versucht, die Typklasse auf verschiedene Arten auszudrücken ( variadicM :: r1 -> r2 und variadicM :: r1 -> MyState r2 ), aber es ist mir nicht gelungen, die Instanzen zu schreiben. Ich habe auch versucht, die obige nicht-monadische Lösung anzupassen, so dass ich irgendwie mit einem Env -> Foo "enden" würde, was ich dann leicht zu einem MyState Foo machen könnte, aber auch kein Glück.

Was folgt ist mein bisher bester Versuch.

%Vor%

Es funktioniert für Foo0 und Foo1, aber nicht darüber hinaus:

%Vor%

(Hier möchte ich die Notwendigkeit für die Annotation loswerden, aber die Tatsache, dass diese Formulierung zwei Instanzen für Foo benötigt, macht das problematisch.)

Ich verstehe die Beschwerde: Ich habe nur eine Instanz, die von Bool -> Char -> Foo nach ID Bool -> MyState (ID Char -> Foo) geht. Aber ich kann das nicht machen Beispiel will es, weil ich MyState irgendwo dort brauche, damit ich kann verwandle das ID Bool in ein Bool .

Ich weiß nicht, ob ich völlig verrückt bin oder was. Ich weiß, dass ich mein grundlegendes Problem (ich möchte meinen Code nicht überall mit den idGet s -Äquivalenten verseuchen) auf verschiedene Arten lösen könnte, z. B. das Erstellen von liftA / liftM -Stil-Funktionen für verschiedene Zahlen von ID-Parametern, mit Typen wie (a -> b -> ... -> z -> ret) -> ID a -> ID b -> ... -> ID z -> MyState ret , aber ich habe zu viel Zeit damit verbracht, darüber nachzudenken. :-) Ich möchte wissen, wie diese variadische Lösung aussehen sollte.

    
Deewiant 29.08.2012, 10:30
quelle

1 Antwort

2

WARNUNG

Verwenden Sie für diese Art von Arbeit vorzugsweise keine variadischen Funktionen. Sie haben nur eine endliche Anzahl von Konstruktoren, also scheinen intelligente Konstruktoren keine große Sache zu sein. Die ~ 10-20 Zeilen, die Sie benötigen, sind viel einfacher und wartungsfreundlicher als eine variadische Lösung. Auch eine anwendungsorientierte Lösung ist viel weniger Arbeit.

WARNUNG

Die Monade / Anwendung in Kombination mit variadischen Funktionen ist das Problem. Das 'Problem' ist der Argumentadditionsschritt, der für die variadische Klasse verwendet wird. Die Basisklasse würde aussehen wie

%Vor%

wo Sie es variabel machen, indem Sie Instanzen des Formulars haben

%Vor%

Was würde brechen, wenn Sie anfangen würden, Monaden zu verwenden. Das Hinzufügen der Monade in der Klassendefinition würde eine Erweiterung des Arguments verhindern (Sie würden :: M (arg - & gt; f) für einige Monaden M erhalten). Das Hinzufügen zum Basisfall würde die Verwendung der Monade in der Erweiterung verhindern, da es (soweit ich weiß) nicht möglich ist, der Erweiterungsinstanz die monadische Einschränkung hinzuzufügen. Für einen Hinweis auf eine komplexe Lösung siehe P.S ..

Die Lösungsrichtung der Verwendung einer Funktion, die in (Env -> Foo) resultiert, ist vielversprechender. Der folgende Code erfordert weiterhin eine :: Foo type-Einschränkung und verwendet zur Vereinfachung eine vereinfachte Version von Env / ID.

%Vor%

Die Typfamilien-Erweiterung wird verwendet, um die Übereinstimmung strenger / besser zu machen. Jetzt die variadic Funktionsklasse.

%Vor%

Die Umgebung Env wird explizit übergeben, im Wesentlichen wird die Reader monad entpackt, was die Probleme zwischen Monaden und variadischen Funktionen verhindert (für die State monad sollte die Auflösungsfunktion eine neue Umgebung zurückgeben). Testen mit app Env Foo1 ID :: Foo führt zu dem erwarteten Foo1 'a' .

P.S. Sie können monadische Variadic-Funktionen erhalten, um (teilweise) zu arbeiten, aber es erfordert, dass Sie Ihre Funktionen (und Ihren Geist) auf eine sehr seltsame Art und Weise verbiegen. Die Art, wie ich solche Dinge zum Laufen bringe, besteht darin, alle variadischen Argumente in eine heterogene Liste zu "falten". Das Auspacken kann dann monadisch erfolgen. Obwohl ich einige Dinge wie diese gemacht habe, rate ich Ihnen dringend davon ab, solche Dinge in tatsächlichem (gebrauchtem) Code zu verwenden, da es schnell unverständlich und nicht mehr zu halten ist (ganz zu schweigen von den Typfehlern, die Sie bekommen würden).

    
Laar 30.08.2012 11:44
quelle