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?
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' –
@ ØrjanJohansen Całkiem poprawnie! – jozefg