我有用haskell写的原始翻译。
此解释器可以正确处理return
语句(see my previous question)。
现在我想将全局状态添加到我的解释器中。
可以从全局代码或函数代码更改此状态
(函数代码使用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"
此代码工作正常,但我不喜欢此代码的evalStatment (FuncCall stmts)
部分。
我将解释器的当前状态传递给runEval
函数,
然后返回修改状态并将其设置为新的解释器状态。
是否可以改进此代码?我可以以某种方式制作函数的代码(FuncCall
)
隐含地操作翻译状态(没有获得当前状态和运行功能)
代码并明确设置解释器的新状态?
答案 0 :(得分:5)
我建议你将基本的Monad改为
type Eval a = ContT Value (State MyState) a
这样,State MyState
部分位于“monad变换器堆栈”的底部,您将能够更轻松地仅拉出上部延续部分而不影响状态。那么FuncCall
案例可以简单地
evalStatment (FuncCall stmts) = lift $ runContT (evalBlock stmts) return
当然这也需要重写其他部分。但并不多,其中大部分实际上变得更简单了!以下是我需要改变的所有部分:
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