构成延续和状态monad变换器的正确方法

ser*_*eyz 8 continuations state haskell monad-transformers

我有用haskell写的原始翻译.这个解释器可以正确处理return语句(参见我之前的问题).

现在我想将全局状态添加到我的解释器中.可以从全局代码或函数代码更改此状态(函数的代码runCont用于提供return逻辑).

代码如下:

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"
Run Code Online (Sandbox Code Playgroud)

这段代码工作正常,但我不喜欢evalStatment (FuncCall stmts)这段代码.我将解释器的当前状态传递给runEval函数,然后返回修改状态并将其设置为新解释器的状态.

是否可以改进此代码?我可以以某种方式使函数的代码(FuncCall)隐式地对解释器的状态进行操作(不获取当前状态和运行函数的代码并明确设置解释器的新状态)?

Ørj*_*sen 5

我建议你改变你的基本Monad

type Eval a = ContT Value (State MyState) a
Run Code Online (Sandbox Code Playgroud)

这样,该State MyState部件位于"monad变换器堆栈"的底部,您将能够更轻松地仅拉出上部延续部分而不影响状态.那么FuncCall情况可以简单地

evalStatment (FuncCall stmts) = lift $ runContT (evalBlock stmts) return
Run Code Online (Sandbox Code Playgroud)

当然,这也需要重写其他部分.但并不多,其中大部分实际上变得更简单了!以下是我需要更改的所有部分:

type Eval a = ContT Value (State MyState) a

runEval eval state = runState (runContT eval return) state 

evalStatment (Return val) = ContT $ \_ -> return (Value val)

evalStatment (FuncCall stmts) = lift $ runContT (evalBlock stmts) return
Run Code Online (Sandbox Code Playgroud)