2013-05-21 18 views
5

W trakcie pisania zadania na uniwersytet mam radość z uczenia się nowych monad Haskella. Yay !!!Rozpakowywanie monady stanu Haskell

Mam funkcję, która typechecks dobrze:

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 

ale gdy ta inna funkcja:

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) 

próbuje je nazwać, to wysadza na linii

s1 <- compile prog 

mówiąc, że nie może on być zgodny z oczekiwanym typem "IO t0" z faktycznym typem "State VarsState String".

Zakładam, że to dlatego, że may_compile_prog zwraca typ IO(), więc spodziewa się tylko rozwinąć informacje o IO? VarsState jest niestandardowym typem danych, którego użyłem z Monadą państwową/

Jednakże, jeśli to jest problem i zakładam, że jest, nie wiem jak przekazać ten prosty ciąg do may_compile_prog. Naprawdę, to wszystko, co chcę zrobić - nadać ciągowi may_compile_prog.

Być może jest jakiś schludny sposób na rozwinięcie tej monady stanu? Być może możliwe jest przepisanie "kompilacji" tak, aby pobierać informacje o monadach stanu podczas działania, ale po prostu zwraca ciąg (nie zawinięty w żadną monadę)?

Proszę dać mi znać, jeśli brakuje mi jakichkolwiek informacji.

Odpowiedz

11

compile prog to akcja w monadzie State VarsState, więc nie można jej używać w postaci bloku IO -do-block. W bloku do-do, wszystkie linie muszą używać tej samej monady, w tym przypadku IO.

Trzeba „run” akcja compile aby uzyskać wynik, jeden z

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

w zależności od tego, czy trzeba

  • zarówno, rezultatu i stan końcowy tylko
  • spowodować
  • tylko stan końcowy

W twoim przypadku potrzebujesz tylko wyniku, więc jest to evalState.

do tego trzeba, aby zapewnić stan początkowy, może to wyglądać

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

ale to pod warunkiem, stan początkowy dla działania compile byłaby taka sama dla wszystkich OK prog s minęły. Jeśli to nie jest właściwe, możesz przekazać stan początkowy jako parametr.

+1

Idealny gość. Z wyjątkiem jednej małej rzeczy, kompiluje się z "let s1 = evalState (compile prog) initialState", a nie "let s1 = evalState $ compile prog initialState". Jeszcze jedno pytanie - czy istnieją takie funkcje dla innych monad? tj. funkcje, które odwijają monadę i zwracają tylko wynik? – nebffa

+0

@nebffa zależy całkowicie od monady, ale tak, niektóre z nich tak.To nie jest coś, co możesz zrobić ogólnie dla wszystkich monad. Innym przykładem może być przerażający 'unsafePerformIO' dla monada' IO', ale nie powinieneś tego używać. –

+0

Ach, tak, pomieszałem nawiasy (najpierw zapomniałem o stanie, a potem zapomniałem wymienić '$' podczas dodawania). Dzięki za heads-up. Dla wielu monad istnieją odpowiednie funkcje, ale nie dla wszystkich. 'IO' to taki, w którym nie ma takiego [no cóż, jest taki, którego nazwa zaczyna się od' unsafe' i naprawdę to naprawdę oznacza]. Istnieją 'runIdentity',' runReader', 'runWriter',' runCont', ... Spójrz na dokumenty dla każdej monady, czy istnieją takie funkcje. –