【问题标题】:Implement imperative return statement in interpreter在解释器中实现命令式返回语句
【发布时间】:2026-01-04 00:55:02
【问题描述】:

我正在尝试在 haskell 中实现简单的命令式语言。

一般来说,我的程序是一个语句列表(如算术表达式、if/then、块语句)。我的评估器有简单的状态:词汇范围堆栈。词法范围只是变量名到值的映射。每次控制流进入函数或块时,我都会推送词法范围,并在控制流离开函数或块时弹出。

但是我在尝试执行return 语句的评估时遇到了问题。 我要做的是在主评估函数中为 return 语句创建一个特殊情况(sources here):

evalStatements :: [Statement] -> Eval MaybeValue

-- nothing to evaluate
evalStatements [] = return Nothing

-- current statement is 'return expr', 
-- evaluate expr and skip the rest of statements
evalStatements (ret@(ReturnStatement _expr):_stmts) =
    leaveLexEnv -- leave current lexical scope
    evalStatement ret >>= return

-- this is last statement in function, eval it and leave lexical scope
evalStatements [stmt] = do
    res <- evalStatement stmt
    leaveLexEnv -- leave current lexical scope
    return res

evalStatements (st:stmts) =
    evalStatement st >> evalStatements stmts >>= return

evalStatement :: Statement -> MaybeValue
evalStatement (ExprStatemet expr) = ...
evalStatement (IfThenStatement expr stmt) = ...

evalStatements 函数中的特殊情况对我来说看起来很难看。而且这种方法不适用于BlockStatement,因为return 语句可以在这个块语句中。 当 return 语句位于多个嵌套块语句中时,另一个问题是恢复词法范围的堆栈。

我想我可以通过在我的评估器中存储一些额外的状态来解决这个问题,但是这种方法看起来不是很好。有些东西告诉我,延续可以在这里帮助我。但我还不太了解延续。

解决此问题的最佳方法是什么?我只需要一个想法,一般概念。

谢谢。

【问题讨论】:

  • 根据 monad 法则,(&gt;&gt;= return) 始终是无操作的。您应该删除这些以简化代码。如果它不是空操作,则您的 Monad 实例 Eval 已损坏。

标签: haskell continuations interpretation


【解决方案1】:

延续可以在这里工作,但它们太过分了。事实上,对于解决任何特定问题,延续几乎总是矫枉过正。

最简单的解决方案是将必要的逻辑放入您的Eval 类型中。如果你让Eval 包含一个用于“这个计算提前返回这个值”的构造函数,然后让你对(&gt;&gt;)(&gt;&gt;=) 的定义尊重这一点,一切都会自动进行。

【讨论】:

  • 我使用StateT 转换器作为我的Eval 类型。它看起来像type Eval a = StateT Environment Identity a。我应该在没有 monad 转换器的情况下实现 Eval monad 吗?
  • @sergeyz 这其实并不简单。如何实现它有很多选择,它们都有不同的权衡。哪一个最好取决于很难从简短的问题描述中提取的微小细节。我的方法是备份并弄清楚Eval 需要支持哪些操作,将它们变成带有operational 库的DSL,然后弄清楚如何为该DSL 编写解释器。
  • 感谢文章链接,对我很有用。