2014-05-11 21 views
5

Ich habe Code, der primitive Programme auswertet. Programm ist eine Liste von Anweisungen (Ausdruck, Block, Return-Anweisung). Ergebnis der Bewertung ist der zuletzt ausgewertete Ausdruck. Auch der Bewerter sollte die Anweisung return richtig behandeln (d. H. Die Bewertung nach dem ersten Auftreten von return stoppen).Code mit Fortsetzungen umschreiben

Um diese Logik zu implementieren, gebe ich spezielle Callback-Funktion (NextStep), die nächsten Auswertungsschritt nach der aktuellen Anweisung machen. Ich nenne nicht im nächsten Schritt, wenn return-Anweisung Handhabung:

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 

Die Frage, wie ist, kann ich diesen Code mit Fortsetzung Monade umschreiben? Ich möchte explizit NextStep Rückruf in evalStmt und evalBlock Funktionen übergeben werden. Ist es möglich?

Antwort

7

Die Übersetzung ist ziemlich mechanisch.

Beachten Sie, dass in der Fortsetzungs-Monade return den Wert in die Fortsetzung einspeist.

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 

Und früh kehrt zu simulieren, ignorieren wir einfach die Fortsetzung zu Return val gegeben und zurück nur den Wert, den wir haben.

+2

Die Monad-Instanz für 'Cont' ist so definiert, dass sie genau so verkettet ist, dass * sollte * einfach äquivalent sein muss zu: evalBlock (st: rest) = evalStmt st >> evalBlock Rest' –

+0

@ ØrjanJohansen Ganz richtig! – jozefg