2014-05-11 22 views
5

Mam trochę kodu, który ocenia prymitywne programy. Program jest listą instrukcji (wyrażenie, blok, instrukcja return). Wynik oceny jest ostatnim ocenianym wyrażeniem. Również ewaluator powinien odpowiednio traktować oświadczenie return (tj. Przestać oceniać po pierwszym wystąpieniu return).Przepisywanie kodu z kontynuacjami

Do realizacji tej logiki przesyłam specjalną funkcję zwrotną (NextStep), która dokonuje następnego kroku oceny po bieżącym wyciągu.

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 

pytanie brzmi, jak mogę przepisać kod z kontynuacją monady: Podczas przenoszenia return ja nie nazywam następny krok? Chcę pozbyć się jawnie przekazanych połączeń zwrotnych NextStep w funkcjach evalStmt i evalBlock. Czy to możliwe?

Odpowiedz

7

Tłumaczenie jest dość mechaniczne.

Należy pamiętać, że w kontynuacji monady, return podaje wartość do kontynuacji.

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 

I symulować wcześniejsze zyski, po prostu zignorować kontynuację nadane Return val i po prostu zwrócić wartość mamy.

+2

Instancja monady dla 'Cont' jest zdefiniowana tak, aby łączyć się dokładnie w taki sposób, aby * powinno * być po prostu równoznaczne z' evalBlock (st: rest) = evalStmt st >> evalBlock rest' –

+0

@ ØrjanJohansen Całkiem poprawnie! – jozefg

Powiązane problemy