Haskell语言学习笔记(10)Writer Monad

Writer 是 Monad

旧的定义
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')
这里旧的定义是指API更改之前的定义,也就是Learn You a Haskell for Great Good!(中文版Haskell趣学指南)这本书中的定义。
  • newtype Writer w a = Writer { runWriter :: (a, w) }
    Writer 类型是个 newtype,也就是对现有类型的封装。该类型有两个类型参数:表示状态的类型参数 w 以及表示结果的类型参数 a。
    Writer 类型封装的是一对值:(a,w),包括结果值 a 和状态值 w。通过 runWriter 字段可以从 Writer 类型中取出这对值。
  • instance (Monoid w) => Monad (Writer w) where
    (Writer w) 类型是 Monad 类型类的一个实例,前提是 w 类型是 Monoid 类型类的一个实例。
    对比 Monad 类型类的定义,可知 return 函数的类型签名为:
    return :: x -> Writer w x
    大致相当于 x -> (x,w)
    而 bind 函数的类型签名为:
    (>>=) :: Writer w a -> (a -> Writer w a) -> Writer w a
    大致相当于 (a,w) -> (a -> (a,w)) -> (a,w)
  • return x = Writer (x, mempty)
    return 函数将结果值设为 x, 状态值设为单位元。
  • (Writer (x,v)) >>= f = let (Writer (y, v')) = f x in Writer (y, v `mappend` v')
    对比函数签名,可知 f 的类型为 a -> Writer w a,大致相当于 a -> (a,w)。
    bind组合函数首先计算 f x,也就是用原来的结果值 x 来调用 f,其结果为新的结果值 y 以及新的状态值 v'。
    然后将结果值设为新的结果值 y,状态值则设为原来的状态值 v 与新的状态值 v' 联结后的值。
新的定义
newtype WriterT w m a = WriterT { runWriterT :: m (a, w) }
instance (Monoid w, Monad m) => Monad (WriterT w m) where
    return a = WriterT $ return (a, mempty)
    m >>= k  = WriterT $ do
        ~(a, w)  <- runWriterT m
        ~(b, w') <- runWriterT (k a)
        return (b, w `mappend` w')
type Writer w = WriterT w Identity
根据新的定义,Writer 类型只是 WriterT 类型的一个特例。

Writer Monad 函数

  • writer (a,w):将一对结果值和状态值封装进Writer Monad。
  • tell w:将状态值设为 w,结果值设为空。
  • listen (a,w):将结果值设为 (a,w),状态值设为 w。
  • pass ((a,f),w):将结果值设为 a,状态值设为 f w。
What is the point of pass and listen in Writer monad? [duplicate]

gcd

import Control.Monad.Trans.Writer

gcd' :: Int -> Int -> Writer [String] Int
gcd' a b
    | b == 0 = do
        tell ["Finished with " ++ show a]
        return a
    | otherwise = do
        tell [show a ++ " mod " ++ show b ++ " = " ++ show (a `mod` b)]
        gcd' b (a `mod` b)

main = mapM_ putStrLn . snd . runWriter $ gcd' 8 3
{-
8 mod 3 = 2
3 mod 2 = 1
2 mod 1 = 0
Finished with 1
=}

gcd

import Control.Monad.Trans.Writer

newtype DiffList a = DiffList { getDiffList :: [a] -> [a] }

toDiffList :: [a] -> DiffList a
toDiffList xs = DiffList (xs++)

fromDiffList :: DiffList a -> [a]
fromDiffList (DiffList f) = f []

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

gcd'' :: Int -> Int -> Writer (DiffList String) Int
gcd'' a b
    | b == 0 = do
        tell (toDiffList ["Finished with " ++ show a])
        return a
    | otherwise = do
        result <- gcd'' b (a `mod` b)
        tell (toDiffList [show a ++ " mod " ++ show b ++ " = " ++ show (a `mod` b)])
        return result

main = mapM_ putStrLn . fromDiffList . snd . runWriter $ gcd'' 8 3
{-
Finished with 1
2 mod 1 = 0
3 mod 2 = 1
8 mod 3 = 2
=}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值