Debugging 在递归单元代码中将单元数转换为跟踪失败

Debugging 在递归单元代码中将单元数转换为跟踪失败,debugging,haskell,trace,Debugging,Haskell,Trace,我有一个较长的递归函数,对于它可以操作的所有不同的数据形式都有很多情况。函数的返回类型为可能,其中无表示失败。如果递归调用的任何子调用失败,则递归调用将失败,因此函数的每种情况下的计算都在do块中完成 到目前为止,我的实现肯定是有缺陷的,因为我给它的输入应该是成功的,而它是失败的,即评估为无。但是,我无法确定哪个子调用的计算结果为Nothing。为了找出发生这种情况的地方,我想跟踪所有递归调用。我的想法是,如果我可以将跟踪调用插入的绑定(也就是说,(>>=)),我就不必将跟踪插入函数的所有情况

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

到目前为止,我的实现肯定是有缺陷的,因为我给它的输入应该是成功的,而它是失败的,即评估为
。但是,我无法确定哪个子调用的计算结果为
Nothing
。为了找出发生这种情况的地方,我想跟踪所有递归调用。我的想法是,如果我可以将
跟踪
调用插入
绑定
(也就是说,
(>>=)
),我就不必将
跟踪
插入函数的所有情况

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
为了实现这个想法,我只是复制了
Maybe
类型及其
Monad
实例的实现,并按照我所述对其进行了修改:我在bind函数中插入了对
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
然后,我基本上可以只更改大函数的返回类型,尽管我不得不更改一些东西,其中我的代码假设
Monad
实际上是
Maybe
catMaybes

我的问题是:是否有一种更优雅、更具组合性的方式来创建一个只需稍加修改的Maybe版本?我听说过“
Monad
transformers”,但我看到的例子似乎并不完全相关


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

是的,您可以将
Writer
Maybe
结合起来解决您的问题

对于monad变压器,顺序通常很重要。哪一个应该是基本单子,哪一个是变压器?这里它是
Writer
应该是基本monad的一个,因为否则一个失败将清除日志(请参阅)

当日志消息的数量增加时,通过
Writer
将列表用作monoid用户将变得效率低下,因为追加函数代价高昂。您可以使用更高效的附加结构,如


有时
Writer
很不方便,因为您只能在计算结束时访问日志消息。您可以使用流式Maad转换器,如“代码>管道< <代码>或<代码>管道< /代码>包,可以在计算的中间记录消息,而不强迫您的单元格生活在<代码> IO < /代码>。< /P> <代码>如果COND则M0ORE……/代码>等效于<代码>守护(非COND);…<代码>。
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