haskell Monad 2 Write monad


Writer Monad



applyLog :: (a,String-> (a -> (b,String)) -> (b,String)   
applyLog (x,log) f = let (y,newLog) = f x in (y,log ++ newLog)
applyLog :: (Monoid m) => (a,m) -> (a -> (b,m)) -> (b,m)   
applyLog (x,log) f = let (y,newLog) = f x in (y,log `mappend` newLog)
一直追加日志, 跟踪记录。


Control.Monad.Writer 这模块含有 Writer w a 的一个型态,里面定义了他 Monad 的 instance,还有一些操作这些值的函数。

newtype Writer w a = Writer { runWriter :: (a, w) }
instance (Monoid w) => Monad (Writer w) where   
    return x = Writer (x, mempty)   
    (Writer (x,v)) >>= f = let (Writer (y, v')) = f x in Writer (y, v `mappend` v')

要把一个 monoid 附加给一个值,只需要定义一个 tuple . 用 monoid w 来 append log。  

>>= 就是 applyLog

ghci> runWriter (return 3 :: Writer String Int)   
(3,"")   
用  do  串接  Writer  型态的值

import Control.Monad.Writer   
   
logNumber :: Int -> Writer [StringInt   
logNumber x = Writer (x, ["Got number: " ++ show x])   
   
multWithLog :: Writer [StringInt   
multWithLog = do   
    a <- logNumber 3   
    b <- logNumber 5   
    return (a*b)

有时候我们就是想要在某个时间点放进某个 Monoid value。tell 正是我们需要的函数。他实作了MonadWriter
 这个 type class,而且在当 Writer 用的时候也能接受一个 monoid value,好比说["This is going on"]
我们能用他来把我们的 monoid value 接到任何一个 dummy value () 上来形成一个 Writer。当我们拿到的结果是 ()
 的时候,我们不会把他绑定到变量上。来看一个 multWithLog的范例:

multWithLog :: Writer [StringInt   
multWithLog = do   
    a <- logNumber 3   
    b <- logNumber 5   
    tell ["Gonna multiply these two"]   
    return (a*b)
ghci> runWriter multWithLog   
(15,["Got number: 3","Got number: 5","Gonna multiply these two"])
return (a*b)  是我们的最后一行,还记得在一个  do  中的最后一行代表整个  do  的结果。如果我们把 tell  摆到
最后,则 
do  的结果则会是  () 。我们会因此丢掉乘法运算的结果。除此之外,log 的结果是不变的。


[] 的 连续 ++  操作, right associative 比较有效率, left associative 没有效率。

当我们用 ++ 来实现 appending 的时候,他必须要走到左边的 list 的尾端,然后把右边的 list 一个个从这边接上。

instance Monoid (DiffList a) where   
    mempty = DiffList (\xs -> [] ++ xs)   
    (DiffList f) `mappend` (DiffList g) = DiffList (\xs -> f (g xs))

Difference list 最酷的地方在于他支持高效的 appending。当我们用 ++ 来实现 appending 的时候,他必须要走到左边
的 list 的尾端,然后把右边的 list 一个个从这边接上。那 difference list 是怎么作的呢?appending 两个 difference list
就像这样

`append` g = \xs -> f (g xs)

f 跟 g 这边是两个函数,他们都接受一个 list 并 prepend 另一串 list。举例来说,如果 f 代表 ("dog"++)
(可以写成 \xs -> "dog" ++ xs)而 g是 ("meat"++),那 f `append` g 就会做成一个新的函数,
等价于:

\xs -> "dog" ++ ("meat" ++ xs)

append 两个 difference list 其实就是用一个函数,这函数先喂一个 list 给第一个 difference list,然后再把结果喂给第二个
difference list。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值