1

晚上好。

Haskell 致力于构建一个简单的语言解释器的项目,因为他的功能性而被选中。

我的问题如下。我有很多函数(这些函数内部的模式匹配)可以实际计算一些东西。我想在屏幕上为我的所有功能在每个功能应用程序中打印一个参数(上下文类型)。

例子:

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'

谢谢你。

4

2 回答 2

5

通常,在每次评估之间打印一些东西的想法在 Haskell 中被认为是不雅的。它以一种让你的代码更难推理的方式交织了纯粹而有效的计算。它也不像您对严格语言所期望的那样具有确定性:惰性意味着您在纯代码中的打印语句可能会在没有警告的情况下被多次评估,或者根本不被评估。

对此的自然解决方案是在排序 Monad 中执行事物。Writer 是一个相当纯的 Monad,它允许您将您的“打印”ctx到缓冲区,然后在稍后阶段实际将该缓冲区写出。懒惰实际上会使缓冲和写出结合在一起,以使其看起来无缝。

另一种选择是将您的计算提升到IOmonad 中,并具有完整的排序和访问所有效果。看起来您的计算涉及通过某种 AST 进行递归——您可以从每个递归调用中返回单子动作IO Int而不是它们的纯表亲Int,并以这种方式在遍历树时构建不纯的计算。

如果您只需要出于调试目的打印 ctx,最后要研究的是该Debug.Trace模块确实包含在纯代码中间不纯地打印的方法。但是,将这些函数包含在生产代码中是一个错误的决定。

于 2013-11-06T23:21:56.163 回答
0

您想要做的事情可以被描述为面向方面的编程,或者(进一步概括)为元编程。Haskell 确实提供了一种元编程工具,称为 Template Haskell,因此这是实现它的一种方法。

但是,如果您有多个相同类型的函数,则可能有更简单的方法。然后你可以把它们放在一个列表中并做

map (\f y ctx -> printAndExec ctx $ f y ctx) list

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

于 2013-11-06T23:30:39.067 回答