2013-04-30 5 views
6

Allgemeines Thema: Während ich finde, dass die Idee, Monaden zusammen zu stapeln, sehr ansprechend ist, habe ich eine Menge Probleme damit, sich vorzustellen, wie der Code ausgeführt wird und welche Befehle die Schichten ausführen sollen. Im Folgenden finden Sie ein Beispiel für einen Stapel: Writer, State, State und Error in keiner bestimmten Reihenfolge (oder?).Wie beurteilen Sie die Reihenfolge der Ausführung von Funktionen in einem MonadT-Stack?

----------------------- 
-- Utility Functions -- 
----------------------- 

type Memory = Map String Int 
type Counter = Int 
type Log  = String 

tick :: (MonadState Counter m) => m() 
tick = modify (+1) 

record :: (MonadWriter Log m) => Log -> m() 
record msg = tell $ msg ++ "; " 

------------------ 
-- MonadT Stack -- 
------------------ 

mStack :: (MonadTrans t, MonadState Memory m, MonadState Counter (t m), MonadError ErrMsg (t m), MonadWriter Log (t m)) => t m Int 
mStack = do 
    tick 
    m <- lift get 
    let x = fromJust (M.lookup "x" m) in x 
    record "accessed memory" 
    case True of 
     True -> return 100 
     False -> throwError "false" 

Bitte beachten Sie in mStack, ob ein Fehler ausgelöst wird oder nicht, hat nichts mit irgendeinem anderen Teil der Funktion zu tun.

nun idealerweise möchte ich die Ausgabe wie folgt aussehen:

(Right 100, 1, "accessed memory", fromList [...]) 

oder allgemein:

(output of errorT, output of stateT Counter, output of writerT, output of StateT Memory) 

Aber ich kann es nicht zu arbeiten. Insbesondere ich versuchte mit dem Stapel als ob Fehler auf der äußerste Schicht ist:

mem1 = M.fromList [("x",10),("y",5)] 
runIdentity $ runWriterT (runStateT (runStateT (runErrorT mStack) 0) mem1) "" 

Aber mir diese Fehlermeldung bekommen:

Couldn't match type `Int' with `Map [Char] Int' 

Die obige Instanz zur Seite, in der Regel, wenn ich nennt:

runMonadT_1 (runMonadT_2 expr param2) param1,

sind die im Zusammenhang Funktionen monadT_2 zuerst ausgeführt, dann wird dieser Ausgang in die Funktionen bezüglich monadT_1 geleitet? Mit anderen Worten, so zwingend der Code in der obigen Funktion mStack aussieht, hängt die Reihenfolge der Ausführung vollständig von der Reihenfolge ab, in der die monadT ausgeführt wird (abgesehen von jeglicher Starrheit in der Struktur, die durch lift eingeführt wurde)?

Antwort

6

Sie haben ein informativer Art Fehler bekommen würde, wenn man versucht hatte, Ihre Berechnung unter Verwendung eines expliziten Monade Transformator Stack eingeben:

mStack :: ErrorT String (StateT (Map String Int) (StateT Int Writer)) Int 

Hätten Sie das getan, wäre ghc die frühere Art Fehler gefangen haben. Der Grund dafür ist, dass Sie die folgenden zwei Befehle innerhalb mStack auf oberster Ebene verwenden:

modify (+1) -- i.e. from `tick` 
... 
yourMap <- lift get 

Wenn Sie dies eine explizite Stack geben sollten, dann würden Sie den Fehler fangen: beide modify und lift get werden Ziel ist die erste Schicht, auf die sie stoßen, die zufällig die gleiche StateT Schicht ist.

modify beginnt von der ErrorT Schicht und geht nach unten, bis sie die äußere StateT Schicht trifft, und kommt zu dem Schluss, dass die äußere StateT muss einen Int Zustand werden. get beginnt von der äußeren Schicht StateT bemerkt, dass es bereits in einer StateT Schicht und ignoriert die innere StateT Schicht vollständig, so dass er kommt zu dem Schluss, dass die äußere Schicht eine StateTMap Speicherung werden müssen.

ghc dann sagt "Was gibt?Diese Schicht kann nicht sowohl Int als auch Map! "Speichern, was den Typfehler erklärt, den Sie bekommen haben. Da Sie jedoch Typklassen anstelle eines konkreten Monaden-Transformatorstapels verwendet haben, konnte ghc nicht wissen, dass dies der Fall war eine Art Fehler in warten, bis Sie einen konkreten Stapel angegeben

die Lösung ist einfach:.. nur ein weiteres lift zu Ihrem get hinzufügen und es wird nun die innere StateT Schicht Ziel wie Sie beabsichtigten

ich bevorzuge persönlich zu vermeiden mtl Klassen vollständig und immer mit einem konkreten Monade Transformatorstapel mit der Bibliothek transformers allein arbeiten Erbose, weil Sie genau wissen müssen, welche Schicht Sie verwenden möchten lift, aber es verursacht weniger Kopfschmerzen auf der Straße.

Verwandte Themen