dem*_*ieu 6 monads haskell monad-transformers
我在" learnyouahaskell "一书中学习过monads .在阅读了作者monad之后,我决定查看Control.Monad.Writer.Class的文档.
在那里,我看到他们也实现了listen
和pass
功能,但我无法理解它们的用途.有人可以给我一个很好的例子,这样我就能理解如何使用listen
和pass
?
这是Control.Monad.Trans.Writer.Strict
定义的代码listen
和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)
Run Code Online (Sandbox Code Playgroud)
WriterT
是一个monad转换器,它Writer
为其他monad 提供了功能,简单Writer
类型定义为 type Writer w = WriterT w Identity
,在Identity
monad中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)
在 a 中,Writer
您无法检查已写入的内容,除非您使用execWriter
or运行(或“解开”)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)
归档时间: |
|
查看次数: |
1010 次 |
最近记录: |