2013-05-21 20 views
5

Nel processo di scrittura di un incarico per l'università mi sto divertendo sempre di più ad imparare nuove monadi Haskell. Sìì!!!Unwrapping the Haskell State Monad

Ho una funzione che typechecks bene:

compile :: Prog -> State VarsState String 
compile [email protected](Prog functions) = do 
    s1 <- sequence (map (translate_func 0) [get_function prog name | name <- [func_name func | func <- functions]]) 
    return $ trace ("here's the program: \n" ++ show prog) $ concat $ s1 

ma quando questa altra funzione:

maybe_compile_prog :: 
    MaybeOK Prog -> String -> IO() 
maybe_compile_prog (Error msg) _ = do 
    putStrLn ("error: " ++ msg) 
maybe_compile_prog (OK prog) modulename = do 
    s1 <- compile prog 
    writeFile (modulename ++ ".m") ((header modulename) ++ s1) 

tenta di chiamare esso, fa saltare in aria alla linea

s1 <- compile prog 

dicendo che non potrebbe corrispondere al tipo previsto "IO t0" con tipo effettivo "State VarsState String".

Presumo che ciò è dovuto al fatto rendimenti maybe_compile_prog tipo IO() in modo che si aspetta di solo unwrap informazioni IO? VarsState è un tipo di dati personalizzato che ho fatto da usare con la monade Stato/

Tuttavia, se questo è il problema e suppongo che sia, non so come trasmettere questa semplice stringa maybe_compile_prog. Davvero, questo è tutto ciò che voglio fare - dare una stringa a forse_compile_prog.

Forse c'è un modo pulito per scartare questa monade di stato? Forse è possibile riscrivere "compile" in modo da contenere alcune informazioni di monade di stato mentre viene eseguito, ma poi restituisce semplicemente una stringa (non racchiusa in nessuna monade)?

Per favore fatemi sapere se mi mancano informazioni.

risposta

11

compile prog è un'azione nella monade State VarsState, quindi non è possibile utilizzarlo in un blocco-IO come tale. In un blocco, tutte le linee devono utilizzare la stessa monade, in questo caso IO.

È necessario "correre" l'azione compile per ottenere il risultato, con uno dei

runState :: State s a -> s -> (a,s) 
evalState :: State s a -> s -> a 
execState :: State s a -> s -> s 

a seconda se è necessario

  • sia, di risultato e stato finale solo
  • tradurrà
  • solo stato finale

Nel tuo caso, vuoi solo il risultato, quindi è evalState.

Per questo è necessario fornire uno stato iniziale, potrebbe sembrare

maybe_compile_prog (OK prog) modulename = do 
    let s1 = evalState (compile prog) initialState 
    writeFile (modulename ++ ".m") ((header modulename) ++ s1) 

ma poi lo stato iniziale previsto per l'azione compile sarebbe lo stesso per tutti i OK prog s passati. Se questa non è la cosa giusta, potresti anche passare lo stato iniziale come parametro.

+1

Tizio perfetto. Tranne che per una piccola cosa, esso viene compilato con "let s1 = evalState (prog prog)", non "let s1 = evalState $ compilare prog initialState". Un'altra domanda: ci sono funzioni come questa per altre monadi? cioè funzioni che scartano la monade e ti restituiscono solo il risultato? – nebffa

+0

@nebffa dipende interamente dalla monade in questione, ma sì, alcuni di loro lo fanno.Non è qualcosa che puoi fare genericamente per tutte le monadi. Un altro esempio potrebbe essere il temuto 'unsafePerformIO' per la monade' IO', ma non dovresti usarlo. –

+0

Ah, sì, ho mescolato le parentesi (ho dimenticato prima lo stato e poi ho dimenticato di scambiare il '$' quando lo aggiungi). Grazie per il testa a testa. Per molte monadi ci sono funzioni corrispondenti, ma non per tutte. "IO" è uno dove non ce n'è uno [beh, ce n'è uno il cui nome inizia con "non sicuro" e in realtà lo significa davvero]. Ci sono 'runIdentity',' runReader', 'runWriter',' runCont', ... Guarda i documenti per ogni monade se ci sono tali funzioni. –