generated at
Writerモナド
write専用のStateモナド
ログ的なおまけはログである必要はなくMonoid型クラスでアレば何でもいい
なので、Stringに限らずモノイドのインスタンスであればなんでも良い感じに扱える
MaybeがJustかNothingかの文脈を持ち回るように、Writerモナドではログの文字列を持ち回り、ログをするタイミングで今まで持っていたログに追記して持ち回る感じ


型の定義
hs
newtype Writer w a = Writer { runWriter :: (a, w) }
w が主の型
a がおまけ。Monoid型クラスのインスタンス
これが String ならログっぽい使い方ができる


定義
hs
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 <> v')
>>= では、 f x の中身を取り出して、元の v と連結した v <> v' y を返している



methods
tell :: MonadWriter w m => w -> m ()
状態 a に追記するアクション
Stateモナドのmodifyに相当
writer :: MonadWriter w m => (a, w) -> m a
普通のタプルからWriter型を作成する


関数
runWriter :: Writer w a -> (a, w)
Writer型の中身のタプルを返す
StateモナドのrunStateに相当する関数
runStateでは初期状態として () を渡すが、writerStateでは初期状態は空なので何も渡さなくていい
hs
import Control.Monad.Writer main = do print $ runWriter $ do -- 初期値はいらない tell "a" tell "b" tell "c" return () -- ((),"abc")
execWriter :: Writer w a -> w
状態だけを返す



動く用いた具体例
ref すごいH本 p.322
hs
import Control.Monad.Writer logNum :: Int -> Writer [String] Int logNum x = writer (x, ["Got number: " ++ show x]) multWithLog :: Writer [String] Int multWithLog = do a <- logNum 3 b <- logNum 5 tell ["Gonna multiply these two"] -- モノイド値のみ追加 return $ a * b -- > runWriter multWithLog -- (15,["Got number: 3","Got number: 5","Gonna multiply these two"])



用途
ログを取るとか


注意
Writerモナド内のモノイドの連結が左結合にならないように注意する
ref すごいH本 p.325
回避するためには差分リストを使う


追記するだけで途中で読むことはできない?
読みたいならStateモナドを使えってことか




参考
スペースリークが発生するのでWriterを使ってはならない