在作家monad中传递和倾听的重点是什么?

dem*_*ieu 6 monads haskell monad-transformers

我在" learnyouahaskell "一书中学习过monads .在阅读了作者monad之后,我决定查看Control.Monad.Writer.Class的文档.

在那里,我看到他们也实现了listenpass功能,但我无法理解它们的用途.有人可以给我一个很好的例子,这样我就能理解如何使用listenpass

zak*_*aps 8

这是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)
Run Code Online (Sandbox Code Playgroud)

WriterT是一个monad转换器,它Writer为其他monad 提供了功能,简单Writer类型定义为 type Writer w = WriterT w Identity,在Identitymonad中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)
Run Code Online (Sandbox Code Playgroud)

现在很清楚,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"])
-}
Run Code Online (Sandbox Code Playgroud)

  • 如果它是重复的,请标记帖子.当你有超过250个代表,你也可以投票结束一个问题作为重复(需要5票).我删除了"这是一个重复的"部分,因为你的(其他写得不好的)答案没有必要. (3认同)

Pet*_*all 6

在 a 中,Writer您无法检查已写入的内容,除非您使用execWriteror运行(或“解开”)monad runWriter。但是,listen在将值附加到pass编写器状态之前,您可以使用检查某些子操作向编写器写入的内容,并且可以使用来修改写入的内容。

考虑以下WriterT用于日志记录的应用程序示例:

import Control.Monad.Writer

-- Monad stack for the application uses IO, wrapped in a logging WriterT
type App a = WriterT [String] IO a

-- utility to write to the log
logMsg :: String -> App ()
logMsg msg = tell [msg]

-- gets an Int from user input (and logs what it does)
getInt :: App Int
getInt = do
    logMsg "getting data"
    n <- liftIO getLine
    logMsg $ "got line: " ++ show n
    return . read $ n

-- application logic that uses getInt and increments the result by 1
app :: App Int
app = do
    n <- getInt
    return $ n + 1

-- main code runs the application and prints the log
main = do
    (res, logs) <- runWriterT app
    print $ "Result = " ++ show res
    putStrLn "Log: "
    mapM_ putStrLn logs
Run Code Online (Sandbox Code Playgroud)

现在,出于某种原因,在里面app,我想知道哪些消息getInt写入了日志。我可以这样做listen

app :: App Int
app = do
    (n, logs) <- listen getInt
    let numLogLines = length logs
    logMsg $ "getInt logged " ++ show numLogLines ++ " lines"
    return $ n + 1
Run Code Online (Sandbox Code Playgroud)