我在 Haskell 中编写了多个编译器,状态 monad 是许多编译器问题的合理解决方案。但是你想让它保持抽象——不要让它明显你正在使用一个 monad。
这是来自 Glasgow Haskell Compiler 的一个示例(我没有编写它;我只是围绕一些边缘工作),我们在其中构建控制流图。以下是制作图表的基本方法:
empyGraph :: Graph
mkLabel :: Label -> Graph
mkAssignment :: Assignment -> Graph -- modify a register or memory
mkTransfer :: ControlTransfer -> Graph -- any control transfer
(<*>) :: Graph -> Graph -> Graph
但正如您所发现的,维持唯一标签的供应充其量是乏味的,因此我们也提供以下功能:
withFreshLabel :: (Label -> Graph) -> Graph
mkIfThenElse :: (Label -> Label -> Graph) -- branch condition
-> Graph -- code in the 'then' branch
-> Graph -- code in the 'else' branch
-> Graph -- resulting if-then-else construct
整个Graph事情是一个抽象类型,翻译者只是愉快地以纯粹的功能方式构造图形,而不知道任何单子正在发生。然后,当最终构建图时,为了将其转换为可以生成代码的代数数据类型,我们为其提供唯一标签,运行状态单子,并提取数据结构。
状态单子隐藏在下面;虽然它没有暴露给客户端,但定义Graph是这样的:
type Graph = RealGraph -> [Label] -> (RealGraph, [Label])
或者更准确一点
type Graph = RealGraph -> State [Label] RealGraph
-- a Graph is a monadic function from a successor RealGraph to a new RealGraph
状态单子隐藏在抽象层后面,一点也不臭!