Haskell:包装函数

时间:2013-11-06 23:10:34

标签: haskell

晚上好。

在构建简单语言解释器的项目上,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'

谢谢。

2 个答案:

答案 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

但是你必须将列表的元素分配给函数名称,这不是那么优雅。