我在书中learnyouahaskell一直在学习单子。 在阅读了作家monad之后,我决定查看Control.Monad.Writer.Class的文档。
在那里,我看到他们还实现了listen
和pass
函数,但我无法理解它们的用途。有人可以给我一个很好的例子,这样我就能理解如何使用listen
和pass
吗?
答案 0 :(得分:8)
以下是定义Control.Monad.Trans.Writer.Strict
和listen
的{{1}}代码:
pass
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)
是一个monad转换器,为其他monad提供WriterT
功能,简单Writer
类型定义为Writer
,在type Writer w = WriterT w Identity
monad中绑定函数(Identity
和<-
)只是变量绑定,所以如果我们分解上面代码的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)
允许您访问Writer monad中Writer操作生成的日志,listen
为您提供了一种方法来更改Writer monad中的日志。
假设您有一个pass
,并且只有在它生成的日志满足某些条件时才想记录操作:
Writer [String]
答案 1 :(得分:3)
在Writer
内部,您无法检查已写入的内容,直到您使用execWriter
或runWriter
运行(或“解包”)monad。但是,您可以使用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
现在,由于某种原因,在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