2013-05-14 15 views
7

Ich habe ein bisschen Schwierigkeiten mit Monade-Transformatoren im Moment. Ich definiere ein paar verschiedene nicht-deterministische Beziehungen, die Transformatoren verwenden. Leider habe ich Probleme zu verstehen, wie man sauber von einem wirkungsvollen Modell zum anderen übersetze.Transformation unter Transformatoren

Angenommen, diese Beziehungen sind "foo" und "bar". Angenommen, "foo" bezieht sich auf As und Bs auf Cs; Angenommen, "bar" verbindet Bs und Cs mit Ds. Wir definieren "bar" in Form von "foo". Um die Sache interessanter zu machen, wird die Berechnung dieser Beziehungen auf verschiedene Arten fehlschlagen. (Da die Bar Beziehung hängt von der foo Beziehung, sind die Fehlerfälle eine Ober.) Ich daher den folgenden Typdefinitionen geben:

data FooFailure = FooFailure String 
data BarFailure = BarSpecificFailure | BarFooFailure FooFailure 
type FooM = ListT (EitherT FooFailure (Reader Context)) 
type BarM = ListT (EitherT BarFailure (Reader Context)) 

dann würde ich erwarten zu können, um die Beziehungen zu den folgenden Funktionssignaturen schreiben :

foo :: A -> B -> FooM C 
bar :: B -> C -> BarM D 

Mein Problem ist, dass, wenn die Definition für „bar“ zu schreiben, ich brauche Raum der Lage sein, erhalten Fehler aus der „foo“ Beziehung und richtig stellen sie in „bar“. Also würde ich mit einer Funktion der Form

convert :: (e -> e') -> ListT (EitherT e (Reader Context) a 
        -> ListT (EitherT e' (Reader Context) a 

in Ordnung sein Ich kann sogar das kleine Tier schreiben, indem die ListT läuft, Mapping auf EitherT und dann die ListT Zusammenbauen (weil es passiert, dass m [a] kann in ListT umgewandelt werden ma). Aber das scheint ... chaotisch.

Es gibt einen guten Grund, warum ich nicht einfach einen Transformator laufen lassen, ein paar Sachen darunter tun und generisch "zurückstecken" kann; der Transformator, den ich lief, könnte Auswirkungen haben und ich kann sie nicht auf magische Weise rückgängig machen. Aber gibt es einen Weg, auf dem ich eine Funktion gerade weit genug in einen Transformatorstapel heben kann, um etwas Arbeit für mich zu tun, also muss ich die oben gezeigte convert Funktion nicht schreiben?

Antwort

3

Ich denke, convert ist eine gute Antwort, und mit Control.Monad.Morph und Control.Monad.Trans.Either es ist (fast) ganz einfach zu schreiben:

convert :: (Monad m, Functor m, MFunctor t) 
      => (e -> e') 
      -> t (EitherT e m) b -> t (EitherT e' m) b 
convert f = hoist (bimapEitherT f id) 

das kleine Problem ist, dass ListT keine Instanz von MFunctor ist. Ich denke, dies ist der Autor boykottieren ListT weil es doesn't follow the monad transformer laws obwohl, weil es einfach eine Typprüfung Instanz

instance MFunctor ListT where hoist nat (ListT mas) = ListT (nat mas) 

Wie dem auch sei, in der Regel einen Blick auf Control.Monad.Morph für den Umgang mit natürlichen Transformationen (von Teilen) Transformator Stapel zu schreiben. Ich würde sagen, das entspricht der Definition, eine Funktion "gerade genug" in einen Stapel zu heben.

+1

Ja, ich habe ListT boykottiert. Verwenden Sie 'pipes' für eine korrekte ListT. Außerdem können Sie 'fmapLT' aus dem Fehlerpaket verwenden, um den linken Wert zu ändern. –

+0

Ich suchte nach 'fmapLT' ...! Aber ich hätte schwören können, dass es "entweder" war und nicht hoogle. –

+0

Ausgezeichnet; Danke euch beiden. Genau darüber habe ich mich gewundert. :) – tvynr