This question already has answers here:
How do `pass` and `listen` work in WriterT?

(4个答案)


5年前关闭。




我已经在learnyouahaskell一书中学习了有关monad的信息。
在阅读了有关作家monad的内容之后,我决定检查Control.Monad.Writer.Class的文档。

在那里,我看到它们还实现了listenpass函数,但我无法理解它们的用途。有人可以给我一个很好的例子,让我理解如何使用listenpass吗?

最佳答案

这是来自Control.Monad.Trans.Writer.Strict的代码,该代码定义了listenpass:

listen :: (Monoid w, Monad m) => WriterT w m a -> WriterT w m (a, w)
listen m = WriterT $ do
    (a, w) <- runWriterT m
    return ((a, w), w)

pass :: (Monoid w, Monad m) => WriterT w m (a, w -> w) -> WriterT w m a
pass m = WriterT $ do
    ((a, f), w) <- runWriterT m
    return (a, f w)
WriterT是一个monad转换器,它为其他monad提供Writer功能,简单的Writer类型定义为type Writer w = WriterT w Identity,在Identity monad中,bind函数(<->>=)只是变量绑定(bind),因此如果我们分解上面代码的monadic部分:
listen :: (Monoid w) => Writer w a -> Writer w (a, w)
listen m = Writer $
    let (a, w) = runWriter m
    in ((a, w), w)

pass :: (Monoid w) => Writer w (a, w -> w) -> Writer w a
pass m = Writer $
    let ((a, f), w) = runWriter m
    in (a, f w)

现在很明显,listen使您可以访问Writer monad内部的Writer操作生成的日志,pass使您可以更改Writer monad内部的日志。

假设您有一个Writer [String],并且仅想在操作所产生的日志满足某些条件时才记录该操作:
deleteOn :: (Monoid w) => (w -> Bool) -> Writer w a -> Writer w a
deleteOn p m = pass $ do
    (a, w) <- listen m
    if p w
        then return (a, id)
        else return (a, const mempty)

-- Or pass alone
deleteOn' :: (Monoid w) => (w -> Bool) -> Writer w a -> Writer w a
deleteOn' p m = pass $ do
    a <- m
    return (a, (\w -> if p w then mempty else w))

logTwo :: Writer [String] ()
logTwo = do
    deleteOn ((> 5) . length . head) $ tell ["foo"]
    deleteOn ((> 5) . length . head) $ tell ["foobar"]
{-
*Main> runWriter logTwo
((),["foo"])
-}

关于haskell - Writer monad的通过和收听的目的是什么? [复制],我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34832072/

10-12 21:01