使用ExceptT与State实现解释器:状态环境获取的单子上下文问题
问题分析与解决方案
你的核心问题是错误地将带Monad上下文的值(m Int)存入了状态环境中,而状态环境的设计目标应该是存储纯的Int值。以下是具体的修正步骤:
1. 明确状态的正确类型
确保你的Env类型定义是纯值映射:
type Ident = String type Env = Map Ident Int -- 这里是Int,不是m Int data EvalError = IdNotDefined deriving (Eq, Show)
2. 修正赋值语句的实现
问题大概率出在AssignStmt的解释逻辑里——你可能直接把表达式求值的m Int结果存进了状态,而没有先提取出纯Int值。正确的赋值实现应该是:
interpAssignStmt :: (MonadState Env m, MonadError EvalError m) => Ident -> Exp -> m () interpAssignStmt ident exp = do -- 先求值表达式,得到纯Int值 val <- interpExp exp -- 将纯Int插入状态环境 modify (\env -> M.insert ident val env)
3. 你的interpIdentExp实现是正确的
当状态环境是Map Ident Int时,你的初始实现完全没问题:
interpIdentExp :: (MonadState Env m, MonadError EvalError m) => Ident -> m Int interpIdentExp ident = do env <- get fromMaybe (throwError IdNotDefined) (M.lookup ident env)
此时M.lookup ident env返回Maybe Int,fromMaybe会把它转换成m Int(因为throwError IdNotDefined是m Int类型,Maybe Int会被自动提升到Monad上下文)。
为什么不要存m Int到状态里
状态环境的作用是存储解释器的纯状态(变量的当前值),而Monad(ExceptT+State)的作用是处理副作用(错误抛出、状态修改)。把m Int存进状态会混淆这两个层次,导致逻辑复杂化,完全没有必要。
内容的提问来源于stack exchange,提问作者string_loginUsername
相关产品推荐
相关产品推荐

