我正在查看this compiler code snippet,但不了解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
是单子,[globals]
是s
,并且evalState ast [globals]
返回类型a
。在哪里可以找到将s
转换为新s
并产生结果a
的状态计算定义?
答案 0 :(得分:1)
函数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
的 first 参数是有状态计算,实际上是ast
成功执行时返回的parse parser source code
是有状态转换{{1} }您正在寻找。
也就是说,值s -> (a, s)
的类型为:
ast
同构为:
ast :: State Env (Contract (Check Type, Env, SourcePos))
所以这是一个有状态的转换,它在由环境(符号表列表)组成的状态下运行并产生合同。 ast :: Env -> (Contract (Check Type, Env, SourcePos), Env)
所做的所有事情就是通过此有状态转换,将一个初始状态/环境(由一个代表全局符号表的单例组成)传递给它,然后产生其最终的合同结果(丢弃符号表的最终列表,因为一旦合同已生成)。
因此,此编译器的设计方式是将代码编译为“抽象语法树”,该语法树不是树状数据结构,实际上是对产生契约的环境状态进行有状态转换的函数; evalState
只是“运行”转换以生成合同。