7

我有一个单子用于计算可能会失败并进行一些日志记录:

f1 :: WriterT [String] (Either String) a

我有一个不会失败但会记录一些日志的功能:

f2 :: Writer [String] b

使用 f2 的日志更新 f1 中的 writer monad 并捕获 f2 计算的输出的最佳方法是什么?目前我正在这样做:

f2result <- (\(r,l) -> do {tell l; return r}) (runWriter f2)

我正在使用 lift 用不同的计算更新内部 monad,因此切换 Writer 和 Either monad 不会解决问题。

4

2 回答 2

4

如果你定义f2了,最简单的方法可能是重构f2,因此它是这样定义的:

 f2 :: Monad m => WriterT [String] m b

这不应该太难,因为Writer w b被定义为WriterT w Identity b,并且Identitymonad 不会给你任何东西。

然后你就可以通过做链接它们f1 >> f2

如果你不能重新定义f2,你总是可以用适当的签名定义你自己的:

 f2' :: Monad m => WriterT [String] m b
 f2' = WriterT . return $ runWriter f2

如果你有一堆f2要包装的东西,你总是可以定义一个函数来为你包装它们

 wrap :: Monad m => Writer w b -> WriterT w m b
 wrap = WriterT . return . runWriter

所以你可以做f1 >> wrap f2a >> wrap f2b >> wrap f2c ...

于 2012-03-14T14:50:17.250 回答
4

作为对 rampion 答案的跟进,您可以改为重构f2 any MonadWriter

f2 :: MonadWriter [String] m => m a

如果不可能改变它的定义,你可以像 rampion 一样包装它:

f2' :: MonadWriter [String] m => m a
f2' = do let (a,w) = runWriter f2
         tell w
         return a

[String]论点MonadWriter需要这个 GHC 杂注:

{-# LANGUAGE FlexibleContexts #-}

与往常一样,编译指示放在模块的顶部。

在评论中,rampion 给出了在此设置中包装函数的版本:

wrap :: MonadWriter w m => Writer w b -> m b
wrap = uncurry (<<) . (return *** tell) . runWriter 
  where (<<) = flip (>>)
于 2012-03-14T15:52:52.097 回答