晚上好。
在构建简单语言解释器的项目上,Haskell因其功能性而被选中。
我的问题如下。我有很多功能(和那些功能中的模式匹配)实际上计算的东西。我想在屏幕上打印一个参数(类型为Context),用于我的所有功能。
例如:
booleanExpression (And b1 b2) ctx = b1' and b2'
booleanExpression (Or b1 b2) ctx = b1' or b2'
...
arithmeticExpression (Multiply a1 a2) ctx = a1' * a2'
....
对于那些函数,我想在每次调用函数时打印ctx。 有一种优雅的简单方法吗?或者我必须用
之类的东西封装每个函数arithmeticExpression (Multiply a1 a2) ctx = printAndExec ctx $ a1' * a2'
谢谢。
答案 0 :(得分:5)
通常,在每次评估之间打印内容的想法在Haskell中被认为是不优雅的。它以一种使得更难以推理代码的方式交织纯粹而有效的计算。它也不像你期望的严格语言那样具有确定性:懒惰意味着纯代码中的打印语句可能会被多次评估,或者根本没有警告。
对此的自然解决方案是在Monad测序中执行。 Writer是一个相当纯粹的Monad,它允许你将ctx
“打印”到缓冲区,然后在稍后阶段实际写出缓冲区。懒惰实际上会使缓冲和写出结合起来,以使其看起来无缝。
另一种选择是将您的计算提升到IO
monad,并具有完整的排序和对所有效果的访问权限。看起来你的计算涉及通过某种类型的AST递归 - 你可以从每个递归调用中返回像IO Int
这样的monadic动作而不是它们的纯cousins Int
,并以这种方式构建一个不纯的计算你遍历你的树。
最后要考虑的是,如果您只需要打印ctx以进行调试,那么Debug.Trace
模块就会包含在纯代码中间进行打印的方法。但是,将这些功能包含在生产代码中是一个糟糕的决定。
答案 1 :(得分:0)
您想要做的事情可以被描述为面向方面的编程,或者(进一步推广)为元编程。 Haskell确实提供了一个名为Template Haskell的元编程工具,因此这是一种方法。
但是,如果您有多个相同类型的函数,可能会有一种更简单的方法。然后你可以将它们放在一个列表中并执行
map (\f y ctx -> printAndExec ctx $ f y ctx) list
但是你必须将列表的元素分配给函数名称,这不是那么优雅。