2014-05-11 14 views
5

Ho un codice che valuta i programmi primitivi. Il programma è una lista di istruzioni (espressione, blocco, dichiarazione di ritorno). Il risultato della valutazione è l'espressione valutata per ultima. Anche il valutatore dovrebbe trattare correttamente la dichiarazione return (vale a dire interrompere la valutazione dopo la prima occorrenza di return).Riscrivente con continuazioni

Per implementare questa logica, passano la funzione di richiamata speciale (NextStep) che effettua il successivo passaggio di valutazione dopo l'istruzione corrente. Io non chiamo prossimo passo durante la manipolazione istruzione return:

data Statement = 
     Expr Int 
    | Block [Statement] 
    | Return Int 
    deriving (Show, Eq) 

data Value = 
     Undefined 
    | Value Int 
    deriving (Show, Eq) 

type NextStep = Value -> Value 

evalStmt :: Statement -> NextStep -> Value 
evalStmt (Expr val) next = 
    let res = Value val 
    in next res 
evalStmt (Block stmts) next = evalBlock stmts next 
evalStmt (Return val) next = Value val 

evalBlock :: [Statement] -> NextStep -> Value 
evalBlock [] next = next Undefined 
evalBlock [st] next = evalStmt st next 
evalBlock (st:rest) next = evalStmt st $ \ _ -> evalBlock rest next 

evalProgram stmts = evalBlock stmts id 

prog1 = [Expr 1, Block [Return 3, Expr 2], Expr 4] 
evalProg1 = evalProgram prog1 -- result will be Value 3 

La domanda è: come posso riscrivere questo codice con prosecuzione monade? Voglio eliminare la richiamata esplicita NextStep nelle funzioni evalStmt e evalBlock. È possibile?

risposta

7

La traduzione è abbastanza meccanica.

Ricordare che nella finestra di continuazione, return invia il valore alla continuazione.

evalStmt :: Statement -> Cont Value Value 
evalStmt (Expr val) = 
    let res = Value val 
    in return res 
evalStmt (Block stmts) = evalBlock stmts 
evalStmt (Return val) = cont $ \_ -> Value val 

evalBlock :: [Statement] -> Cont Value Value 
evalBlock [] = return Undefined 
evalBlock [st] = evalStmt st 
evalBlock (st:rest) = evalStmt st >> evalBlock rest 

evalProgram :: [Statement] -> Value 
evalProgram stmts = runCont (evalBlock stmts) id 

E per simulare i rendimenti prime, dobbiamo solo ignorare la continuazione dato a Return val e solo restituiscono il valore che abbiamo.

+2

L'istanza monad per 'Cont' è definita per concatenare esattamente in questo modo, in modo che * dovrebbe * essere semplicemente equivalente a' evalBlock (st: rest) = evalStmt st >> evalBlock resto' –

+0

@ ØrjanJohansen Giusto! – jozefg

Problemi correlati