问题描述
我正在为一种简单的编程语言编写解释器。它有两个阶段:解析器和评估器。解析器读取文本文件并使用表达式、值等创建树。它还进行一些验证。这是我正在使用的 monad 堆栈:
type Type = IntT | BoolT | CharT
data FunEnv = [String]
data VarEnv = [(String,Type)]
type ParserEnv r = StateT (FunEnv,VarEnv) (Except String) r
函数环境存储已定义函数的名称,变量环境存储已定义变量的类型。当解析器读取文件并找到函数或变量定义时,它将它们添加到状态中。解析器也可能失败,这就是我使用 Except
monad 的原因。
在那之后,是评估者。求值器也需要一个类似的 monad 堆栈,但它不需要存储变量的类型,而是需要存储它们正在存储的值:
type Value = IntV Int | BoolV Bool | CharV Char
data FunEnv = [String]
data VarEnv = [(String,Value)]
type EvaluatorEnv r = StateT (FunEnv,VarEnv) (Except String) r
所以,为了能够复用之前的栈,我想到了重构,使其更通用:
data FunEnv = [String]
type VarEnv a = [(String,a)]
type Env a r = StateT (FunEnv,VarEnv a) (Except String) r
现在 FunEnv
和 VarEnv
更通用,因此我可以使用相同的 monad 堆栈为解析器和求值器定义类型:
type ParserEnv r = Env Type r
type EvaluatorEnv r = Env Value r
尽管这使与 monad 在低级别交互的函数类型有点复杂,但它是有效的。然而,问题是评估器必须能够执行 IO 操作,所以我需要 IO monad 作为我的堆栈的核心。再一次,我可以概括我的堆栈来说明这一点:
type Env a m r = StateT (FunEnv,VarEnv a) (ExceptT String m) r
如您所见,我添加了一个参数 m
,它是堆栈核心的 monad,我使用的是 ExceptT
而不是 Except
。现在,我可以重新定义以 Identity
为核心的解析器 monad,以及以 IO
为核心的评估器 monad:
type ParserEnv r = Env Type Identity r
type EvaluatorEnv r = Env Value IO r
我在这种方法中看到的一个缺点是,我需要为 mtl
monad 使用 Identity
包,但更重要的是,它使低级函数的类型更加复杂。用于获取与变量关联的内容的通用函数的类型如下所示:
getvariable :: Monad m => String -> Env a m (Maybe a)
也许我想得太多了,我的解决方案没问题,但我很感激第二个意见。 有没有更好的方法来使这个 monad 堆栈通用?还是行为差异太大,我应该分别实现两次?
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)