2013-04-30 9 views
6

Tema generale: Mentre trovo che l'idea di impilare le monadi sia molto allettante, ho molti problemi a immaginare come viene eseguito il codice e quali sono gli ordini appropriati per eseguire i livelli. Di seguito è riportato un esempio di stack: Writer, State, State ed Error, in nessun ordine particolare (o c'è?).Come si ragiona sull'ordine di esecuzione delle funzioni in uno stack monadT?

----------------------- 
-- 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" 

Si prega di notare in mStack, se viene generato un errore o no non ha nulla a che fare con qualsiasi altra parte della funzione.

Ora idealmente voglio l'uscita a guardare come questo:

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

o in generale:

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

Ma non riesco a farlo funzionare. In particolare, ho provato a fare funzionare la pila come se l'errore è lo strato più esterno:

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

Ma sto ottenendo questo messaggio di errore:

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

È possibile che questo esempio a parte, in generale, quando ho chiamo:

runMonadT_1 (runMonadT_2 expr param2) param1,

sono le funzioni relative a monadT_2 viene eseguito per primo, quindi quell'output viene collegato alle funzioni relative a monadT_1? Quindi, in altre parole, per quanto il codice appaia imperativo nella funzione di cui sopra mStack, l'ordine di esecuzione dipende interamente dall'ordine in cui viene eseguito il monadT (oltre a qualsiasi rigidità nella struttura introdotta da lift)?

risposta

6

Si sarebbe ottenuto un errore di tipo più informativo se avessi provato a digitare il calcolo utilizzando uno stack esplicito trasformatore di monade:

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

se aveste fatto che, ghc avrebbe catturato l'errore di tipo in precedenza. Il motivo è che si utilizzano i seguenti due comandi all'interno mStack in alto a più livelli:

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

Se si dovesse dare a questo uno stack esplicito, allora si sarebbe prendere l'errore: sia modify e lift get stanno andando per indirizzare il primo strato StateT che incontrano, che è lo stesso livello StateT.

modify inizia dallo strato ErrorT e procede verso il basso fino a raggiungere la StateT strato esterno, e conclude che l'esterno StateT deve utilizzare uno stato Int. get inizia dal livello StateT esterno, rileva che si trova già in un livello StateT e ignora completamente il livello interno StateT, quindi conclude che il livello esterno StateT deve memorizzare uno Map.

ghc quindi dice "Che cosa dà?Questo livello non può memorizzare sia un Int e un Map! ", Che spiega l'errore di tipo che hai ottenuto.Tuttavia, perché hai usato classi di tipo invece di uno stack di monad concreto, non c'era modo che ghc potesse sapere che questo era un errore di tipo in attesa fino a quando è stata specificata una pila di cemento

la soluzione è semplice:.. basta aggiungere un altro lift al get e sarà ora obiettivo il StateT strato interno, come si intende

io personalmente preferisco evitare Le classi mtl sono interamente e funzionano sempre con una pila di trasformatori monad in calcestruzzo utilizzando la libreria transformers da sola. erbose perché devi essere preciso sul livello che vuoi usare usando lift, ma provoca meno mal di testa lungo la strada.