class (Monoid w, Monad m) => MonadWriter w m | m -> w where writer :: (a,w) -> m a writer ~(a, w) = do tell w return a tell :: w -> m () tell w = writer ((),w) listen :: m a -> m (a, w) pass :: m (a, w -> w) -> m a listens :: MonadWriter w m => (w -> b) -> m a -> m (a, b) listens f m = do ~(a, w) <- listen m return (a, f w) censor :: MonadWriter w m => (w -> w) -> m a -> m a censor f m = pass $ do a <- m return (a, f) instance (Monoid w, Monad m) => MonadWriter w (Lazy.WriterT w m) where writer = Lazy.writer tell = Lazy.tell listen = Lazy.listen pass = Lazy.pass instance (Monoid w, Monad m) => MonadWriter w (Strict.WriterT w m) where writer = Strict.writer tell = Strict.tell listen = Strict.listen pass = Strict.pass
class (Monoid w, Monad m) => MonadWriter w m | m -> w where
MonadWriter 是個類型類,它爲 WriterT, RWST 等具備 Writer 功能的 Monad 定義了通用接口。
所謂 Writer 功能是指將程序的行爲記錄下來,也能夠理解爲日誌。
MonadWriter 包含四個函數:writer, tell, listen, pass。
writer (a,w) 記錄輸出信息 w,返回結果值 a。
tell w 記錄輸出信息 w,返回結果值 ()。
listen m 將結果值設置爲由結果值和輸出信息組成的二元組 (a, w),輸出信息不變。
pass m 將結果值解析爲結果值 a 和函數 f 組成的二元組,設置結果值 a, 而後對輸出信息 w 調用函數 f。
另外同一個模塊中還定義了 listens 和 censor 函數。
listens f m 將結果值設置爲由結果值和輸出信息(調用函數 f 修改後的返回值)組成的二元組 (a, f w),輸出信息不變。
censor f m 對輸出信息 w 調用函數 f,結果值不變。
What's the 「|」 for in a Haskell class definition?app
instance (Monoid w, Monad m) => MonadWriter w (Lazy.WriterT w m) where
writer = Lazy.writer
對於 WriterT 這個Monad轉換器來講,writer等函數的定義均由 WriterT 模塊來提供。注意這裏點運算符的含義不是函數的合成而是受限名字。
Hackage - Where is the MonadReader implementation for ReaderT defined?函數
newtype WriterT w m a = WriterT { runWriterT :: m (a, w) } instance (Monoid w, Monad m) => Monad (WriterT w m) where return a = writer (a, mempty) m >>= k = WriterT $ do ~(a, w) <- runWriterT m ~(b, w') <- runWriterT (k a) return (b, w `mappend` w') writer :: (Monad m) => (a, w) -> WriterT w m a writer = WriterT . return
證實 WriterT r m 符合Monad法則: 1. return a >>= f ≡ f a return a >>= f ≡ writer (a, mempty) >>= f ≡ (WriterT . return) (a, mempty) >>= f ≡ WriterT (m (a, mempty)) >>= f ≡ WriterT $ do {~(a, w) <- runWriterT (WriterT (m (a, mempty))); ~(b, w') <- runWriterT (f a); return (b, w <> w')} ≡ WriterT $ do {~(a, w) <- m (a, mempty)); ~(b, w') <- runWriterT (f a); return (b, w <> w')} ≡ WriterT $ do {~(b, w') <- runWriterT (f a); return (b, empty <> w')} ≡ WriterT $ do {~(b, w') <- runWriterT (f a); return (b, w')} ≡ WriterT $ runWriterT (f a) ≡ f a 2. m >>= return ≡ m m = WriterT (n (a w)) m >>= return ≡ WriterT (n (a w)) >>= return ≡ WriterT $ do {~(a, w) <- runWriterT (WriterT (n (a, w))); ~(b, w') <- runWriterT (return a); return (b, w <> w')} ≡ WriterT $ do {~(a, w) <- n (a, w)); ~(b, w') <- runWriterT (WriterT (m (a, mempty))); return (b, w <> w')} ≡ WriterT $ do {~(b, w') <- runWriterT (WriterT (m (a, mempty))); return (b, w <> w')} ≡ WriterT $ do {~(b, w') <- (m (a, mempty)); return (b, w <> w')} ≡ WriterT $ do {return (a, w <> empty)} ≡ WriterT $ do {return (a, w)} ≡ WriterT (n (a w)) ≡ m 3. (m >>= f) >>= g ≡ m >>= (\x -> f x >>= g) 假設 m = WriterT (n (a1, w1)), f a1 = WriterT (n (a2, w2)), g a2 = WriterT (n (a3, w3)) (m >>= f) >>= g ≡ (WriterT $ do {~(a, w) <- runWriterT m; ~(b, w') <- runWriterT (f a); return (b, w <> w')}) >> g ≡ WriterT $ do {~(a, w) <- runWriterT (WriterT $ do {~(a, w) <- runWriterT m; ~(b, w') <- runWriterT (f a); return (b, w <> w')}); ~(b, w') <- runWriterT (g a); return (b, w <> w')} ≡ WriterT $ do {~(a, w) <- do {~(a, w) <- runWriterT m; ~(b, w') <- runWriterT (f a); return (b, w <> w')}; ~(b, w') <- runWriterT (g a); return (b, w <> w')} ≡ WriterT $ do {~(a, w) <- do {~(a, w) <- runWriterT (WriterT (n (a1, w1))); ~(b, w') <- runWriterT (f a); return (b, w <> w')}; ~(b, w') <- runWriterT (g a); return (b, w <> w')} ≡ WriterT $ do {~(a, w) <- do {~(a, w) <- n (a1, w1); ~(b, w') <- runWriterT (f a); return (b, w <> w')}; ~(b, w') <- runWriterT (g a); return (b, w <> w')} ≡ WriterT $ do {~(a, w) <- do {~(b, w') <- runWriterT (f a1); return (b, w1 <> w')}; ~(b, w') <- runWriterT (g a); return (b, w <> w')} ≡ WriterT $ do {~(a, w) <- do {~(b, w') <- runWriterT (WriterT (n (a2, w2))); return (b, w1 <> w')}; ~(b, w') <- runWriterT (g a); return (b, w <> w')} ≡ WriterT $ do {~(a, w) <- do {~(b, w') <- n (a2, w2); return (b, w1 <> w')}; ~(b, w') <- runWriterT (g a); return (b, w <> w')} ≡ WriterT $ do {~(a, w) <- n (a2, w1 <> w2); ~(b, w') <- runWriterT (g a); return (b, w <> w')} ≡ WriterT $ do {~(b, w') <- runWriterT (g a2); return (b, (w1 <> w2) <> w')} ≡ WriterT $ do {~(b, w') <- runWriterT (WriterT (n (a3, w3))); return (b, (w1 <> w2) <> w')} ≡ WriterT $ do {~(b, w') <- n (a3, w3); return (b, (w1 <> w2) <> w')} ≡ WriterT (n (a3, (w1 <> w2) <> w3)) m >>= (\x -> f x >>= g) ≡ WriterT $ do {~(a, w) <- runWriterT m; ~(b, w') <- runWriterT ((\x -> f x >>= g) a); return (b, w <> w')} ≡ WriterT $ do {~(a, w) <- runWriterT m; ~(b, w') <- runWriterT (f a >>= g); return (b, w <> w')} ≡ WriterT $ do {~(a, w) <- runWriterT m; ~(b, w') <- runWriterT (WriterT $ do {~(a, w) <- runWriterT (f a); ~(b, w') <- runWriterT (g a); return (b, w <> w')}); return (b, w <> w')} ≡ WriterT $ do {~(a, w) <- runWriterT m; ~(b, w') <- do {~(a, w) <- runWriterT (f a); ~(b, w') <- runWriterT (g a); return (b, w <> w')}; return (b, w <> w')} ≡ WriterT $ do {~(a, w) <- runWriterT (WriterT (n (a1, w1))); ~(b, w') <- do {~(a, w) <- runWriterT (f a); ~(b, w') <- runWriterT (g a); return (b, w <> w')}; return (b, w <> w')} ≡ WriterT $ do {~(a, w) <- n (a1, w1); ~(b, w') <- do {~(a, w) <- runWriterT (f a); ~(b, w') <- runWriterT (g a); return (b, w <> w')}; return (b, w <> w')} ≡ WriterT $ do {~(b, w') <- do {~(a, w) <- runWriterT (f a1); ~(b, w') <- runWriterT (g a); return (b, w <> w')}; return (b, w1 <> w')} ≡ WriterT $ do {~(b, w') <- do {~(a, w) <- runWriterT (WriterT (n (a2, w2))); ~(b, w') <- runWriterT (g a); return (b, w <> w')}; return (b, w1 <> w')} ≡ WriterT $ do {~(b, w') <- do {~(a, w) <- n (a2, w2); ~(b, w') <- runWriterT (g a); return (b, w <> w')}; return (b, w1 <> w')} ≡ WriterT $ do {~(b, w') <- do {~(b, w') <- runWriterT (g a2); return (b, w2 <> w')}; return (b, w1 <> w')} ≡ WriterT $ do {~(b, w') <- do {~(b, w') <- runWriterT (WriterT (n (a3, w3))); return (b, w2 <> w')}; return (b, w1 <> w')} ≡ WriterT $ do {~(b, w') <- do {~(b, w') <- n (a3, w3); return (b, w2 <> w')}; return (b, w1 <> w')} ≡ WriterT $ do {~(b, w') <- n (a3, w2 <> w3); return (b, w1 <> w')} ≡ WriterT (n (a3, w1 <> (w2 <> w3)) 根據 Monoid 的法則:(x <> y) <> z = x <> (y <> z) WriterT (n (a3, (w1 <> w2) <> w3)) ≡ WriterT (n (a3, w1 <> (w2 <> w3))
instance (Monoid w) => MonadTrans (WriterT w) where lift m = WriterT $ do a <- m return (a, mempty)
證實 WriterT 中 lift 函數的定義符合 lift 的法則。 1. lift . return ≡ return lift . return $ a ≡ lift (m a) ≡ WriterT $ do {a <- m a; return (a, mempty)} ≡ WriterT $ m (a, mempty) ≡ return a 2. lift (m >>= f) ≡ lift m >>= (lift . f) 假設 m = n a 而且 f a = n b 因而 m >>= f = n b lift (m >>= f) ≡ lift (n b) ≡ WriterT $ do {a <- n b; return (a, empty)} ≡ WriterT $ n (b, empty) lift m >>= (lift . f) ≡ (WriterT $ do {a <- n a; return (a, empty)}) >>= (\x -> WriterT $ do {a <- f x; return (a, empty)}) ≡ (WriterT $ n (a empty)) >>= (\x -> WriterT $ do {a <- f x; return (a, empty)}) ≡ WriterT $ do {runWriterT $ WriterT $ do {a <- f a; return (a, empty <> empty)}} ≡ WriterT $ do {runWriterT $ WriterT $ do {a <- n b; return (a, empty)}} ≡ WriterT $ do {runWriterT $ WriterT $ n (b empty)} ≡ WriterT $ n (b empty)
tell :: (Monad m) => w -> WriterT w m () tell w = writer ((), w) listen :: (Monad m) => WriterT w m a -> WriterT w m (a, w) listen m = WriterT $ do ~(a, w) <- runWriterT m return ((a, w), w) listens :: (Monad m) => (w -> b) -> WriterT w m a -> WriterT w m (a, b) listens f m = WriterT $ do ~(a, w) <- runWriterT m return ((a, f w), w) pass :: (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) censor :: (Monad m) => (w -> w) -> WriterT w m a -> WriterT w m a censor f m = WriterT $ do ~(a, w) <- runWriterT m return (a, f w) execWriterT :: (Monad m) => WriterT w m a -> m w execWriterT m = do (_, w) <- runWriterT m return w mapWriterT :: (m (a, w) -> n (b, w')) -> WriterT w m a -> WriterT w' n b mapWriterT f m = WriterT $ f (runWriterT m)
Prelude Control.Monad.Writer> runWriterT $ tell "abc" ((),"abc") Prelude Control.Monad.Writer> runWriterT $ tell "abc" >> tell "def" ((),"abcdef") Prelude Control.Monad.Writer> runWriterT $ tell "abc" >> return 3 (3,"abc") Prelude Control.Monad.Writer> runWriterT $ tell "abc" >> listen (tell "def") (((),"def"),"abcdef") Prelude Control.Monad.Writer> runWriterT $ listens (++ "def") $ tell "abc" (((),"abcdef"),"abc") Prelude Control.Monad.Writer> runWriterT $ pass $ tell "abc" >> return (0, (++ "def")) (0,"abcdef") Prelude Control.Monad.Writer> runWriterT $ censor (++ "def") $ tell "abc" ((),"abcdef")
type Writer w = WriterT w Identity runWriter :: Writer w a -> (a, w) runWriter = runIdentity . runWriterT execWriter :: Writer w a -> w execWriter m = snd (runWriter m) mapWriter :: ((a, w) -> (b, w')) -> Writer w a -> Writer w' b mapWriter f = mapWriterT (Identity . f . runIdentity)
Writer Monad 是 WriterT Monad(轉換器) 的一個特例。日誌
import Control.Monad.Trans.Writer logNumber :: Int -> Writer [String] Int logNumber x = writer (x, ["Got number: " ++ show x]) logNumber2 :: Int -> Writer [String] Int logNumber2 x = do tell ["Got number: " ++ show x] return x multWithLog :: Writer [String] Int multWithLog = do a <- logNumber 3 b <- logNumber 5 tell ["multiplying " ++ show a ++ " and " ++ show b ] return (a*b) main :: IO () main = print $ runWriter multWithLog -- (15,["Got number: 3","Got number: 5","multiplying 3 and 5"])