haskell - 如何理解这个 State Monad Haskell 代码片段中的 evalState?
问题描述
我正在查看此编译器代码片段,但不明白什么evalState
是 State Monad 的新手。
compileToAst :: FilePath -> String -> Either Errors (Contract (Check Type, Env, SourcePos))
compileToAst source code = case parse parser source code of
Right ast -> let ast' = evalState ast [globals]
errors = lefts $ map ann $ toList ast'
ann (a, _, pos) = a `extend` sourcePosPretty pos
in if null errors then Right ast' else Left errors
Left err -> Left [(SyntaxError $ parseErrorTextPretty err, sourcePosPretty . NE.head $ errorPos err)]
假设有状态计算的形式是s -> (a, s)
,
ast
是 monad,[globals]
是s
,并evalState ast [globals]
返回 type a
。我在哪里可以找到转换s
为新的s
和产生结果的有状态计算定义a
?
解决方案
该函数evalState
具有类型:
evalState :: State s a -> s -> a
第一个参数的类型,即State s a
,实际上与函数类型同构s -> (a, s)
。这正式意味着存在两个函数可以在它们之间进行转换:
runState :: State s a -> (s -> (a, s))
state :: (s -> (a, s)) -> State s a
如果你应用其中一个函数然后另一个,你会得到你开始的东西(即,它们是逆函数,它们的组合是恒等函数)。
不太正式,这意味着无论您在哪里看到State s a
都可以假装它是类型s -> (a, s)
,反之亦然,因为您可以使用这些实用函数runState
和state
.
因此,所做evalState
的只是采用与有状态计算同构的第一个参数,s -> (a, s)
并使用其第二个参数给出的初始状态来运行它。然后它丢弃最终状态s
并产生计算的最终结果。
由于它是有状态计算的第一个参数,因此它实际上是成功时返回的,这就是您正在寻找的有状态转换。evalState
ast
parse parser source code
s -> (a, s)
也就是说,该值ast
具有以下类型:
ast :: State Env (Contract (Check Type, Env, SourcePos))
它同构于:
ast :: Env -> (Contract (Check Type, Env, SourcePos), Env)
所以它是一个有状态的转换,它在一个由环境(符号表列表)组成的状态上运行并产生一个合约。所做的只是将这个有evalState
状态的转换传递给一个初始状态/环境,该状态/环境由一个表示全局符号表的单例组成,然后产生其最终的合约结果(丢弃符号表的最终列表,因为一旦生成合约,它就不再重要了)。
因此,这个编译器的设计方式是将代码编译成“抽象语法树”,而不是树状数据结构,实际上是一个函数,对产生合约的环境状态进行状态转换;evalState
只需“运行”转换以生成合同。
推荐阅读
- c# - 全局 WPF 工具包颜色选择器标准颜色
- amazon-web-services - DynamoDB 仅检索属性值
- tfs - 结帐时自动锁定文件(VS2017 w/On Premises TFS)
- c# - 将两个 foreach 语句合二为一
- c# - RedirectToAction 被命中但不重定向
- mongodb - 在 mongo 容器 shell 中输入一些命令后,终端卡住了
- arduino - Sim800L 时间没有更新到网络时间
- c++ - 无法使用 MinGW-w64 链接到 GLFW 3
- javascript - 数据更改时 DataTable 不更新
- apache-spark - 数据框的多维数据集/汇总功能,但跳过火花中少数记录的列的总和