转换Monad以跟踪递归monadic代码中的失败

时间:2014-05-10 06:08:15

标签: debugging haskell tracing

我有一个很长的递归函数,它可以操作所有不同的数据形式。函数的返回类型是Maybe,其中Nothing表示失败。如果任何子调用失败,递归调用将失败,因此函数的每种情况下的计算都在do块中完成。

到目前为止我的实现肯定是错误的,因为我给它的输入应该成功,并且它失败了,即评估为Nothing。但是,我无法确定哪个子查询正在评估Nothing。为了确定这发生的位置,我想跟踪所有的递归调用。我有一个想法,如果我可以在trace的{​​{1}} Maybe(即bind)中插入(>>=)来电,我就不必插入{{1}进入函数的所有情况。

为了实现这个想法,我只是复制了trace类型的实现及其Maybe实例,并按照我的描述对其进行了修改:我在绑定函数中插入了对Monad的调用

trace

然后我几乎只能更改大函数的返回类型,虽然我不得不改变一些事情,我的代码假设data TraceMaybe a = TNothing | TJust a deriving (Eq, Ord, Show) tracet :: TraceMaybe a -> TraceMaybe a tracet TNothing = trace "monad failed" TNothing tracet x = trace "monad good" x instance Monad TraceMaybe where (TJust x) >>= k = tracet $ k x TNothing >>= _ = TNothing (TJust _) >> k = tracet k TNothing >> _ = TNothing return = TJust fail m = TNothing 实际上是MonadMaybe ,例如),

我的问题是:是否有一种更优雅,更具创意的方式来创建一个只有稍微修改过的Maybe版本?我听说过“catMaybes变形金刚”,但我见过的例子似乎并不完全相关。

我想知道是否有某种方法可以将Writer和Maybe结合起来。

1 个答案:

答案 0 :(得分:2)

是的,您可以合并WriterMaybe来解决您的问题。

使用monad变形金刚,订单通常很重要。哪个应该是基地monad,哪个是变压器?这里Writer应该是基础monad,因为否则失败会消除日志(参见this other answer)。

import Control.Monad
import Control.Monad.Writer.Strict
import Control.Monad.Trans.Maybe

computation :: Int -> MaybeT (Writer [String]) Int
computation i = do
    foo <- return 5  
    lift $ tell ["This is a log message"]
    if i == 3
        then mzero -- from MonadPlus, of which MaybeT is an instance
        else return $ i + foo 

*Main> runWriter . runMaybeT $ computation 3
(Nothing,["This is a log message"])

*Main> runWriter . runMaybeT $ computation 2
(Just 7,["This is a log message"])

对变形金刚的一个很好的介绍是论文"Monad Transformers Step by Step"

mtl包有类型类,可以让你避免在许多情况下使用lift代码,并且还可以编写更多通用签名(说&#34; monad应该有{{ 1}}功能&#34;而不必指定确切的monad堆栈。)

MonadWriter

当日志消息的数量增加时,使用列表作为computation' :: (MonadWriter [String] m) => Int -> MaybeT m Int computation' i = do foo <- return 5 tell ["This is a log message"] -- no explicit lift if i == 3 then mzero else return $ i + foo 的幺半群用户变得效率低下,因为追加功能成本很高。您可以转向具有更高效附加效果的结构,例如dlists

有时Writer不方便,因为您只能访问计算的 end 处的日志消息。您可以使用Writerconduit包中的流式monad变换器,以便能够在计算过程中记录消息,而不必强迫您的monad存在于pipes。< / p>