Ho un interprete primitivo scritto in haskell. Questo interprete può gestire correttamente le dichiarazioni return
(see my previous question).Il modo giusto per comporre i continuatori di stato e di continuazione
Ora voglio aggiungere lo stato globale al mio interprete. Questo stato può essere modificato dal codice globale o dal codice della funzione (il codice della funzione viene eseguito con runCont
per fornire la logica return
).
Il codice è presentato qui:
import Control.Monad.Cont
import Control.Monad.State
type MyState = String
data Statement = Return Int | GetState | SetState MyState | FuncCall [Statement] deriving (Show)
data Value = Undefined | Value Int | StateValue MyState deriving (Show)
type Eval a = StateT MyState (Cont (Value, MyState)) a
runEval ::(Eval Value) -> MyState -> (Value, MyState)
runEval eval state = runCont (runStateT eval state) id
evalProg :: [Statement] -> Value
evalProg stmts = fst $ runEval (evalBlock stmts) $ ""
evalBlock :: [Statement] -> Eval Value
evalBlock [] = return Undefined
evalBlock [stmt] = evalStatment stmt
evalBlock (st:stmts) = evalStatment st >> evalBlock stmts
evalStatment :: Statement -> Eval Value
evalStatment (Return val) = do
state <- get
lift $ cont $ \_ -> (Value val, state)
evalStatment (SetState state) = put state >> return Undefined
evalStatment (FuncCall stmts) = do
-- I don't like this peace of code
state <- get
(value, newState) <- return $ runEval (evalBlock stmts) $ state
put newState
return value
evalStatment GetState = get >>= return . StateValue
test2 = evalProg [SetState "Hello", FuncCall [SetState "Galaxy", Return 3], GetState] -- result is StateValue "Galaxy"
Questo codice funziona bene, ma non mi piace la parte evalStatment (FuncCall stmts)
di questo codice. Passaggio allo stato attuale dell'interprete per la funzione runEval
, quindi ripristinare lo stato modificato e impostarlo come nuovo stato dell'interprete.
È possibile migliorare questo codice? Posso in qualche modo rendere il codice della funzione (FuncCall
) funzionare in modo implicito sullo stato dell'interprete (senza ottenere lo stato corrente e il codice della funzione di esecuzione e impostare esplicitamente il nuovo stato dell'interprete)?
Hai solo bisogno di 'sequenza :: Monad m => [m a] -> m [a]'? Possibilmente seguito da 'liftM last :: Monad m => m [a] -> m a'? –