テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

Stateモナドのjoin

モナドの性質の一つとして,

m >>= f = join (fmap f m)

を満たすことがあるようです. H本には(>>=)を実装するより, fmapしたものをjoinする方が, 多くの場合簡単だと書いてあります (p. 348). Foldableにも似た話があります. Foldableは, foldrを実装するか, あるいはfoldMapを実装するかいずれかによってインスタンスを実装出来ます.

foldMap :: (Monoid m, Foldable t) => (a -> m) -> t a -> m

これは, 中身をmonoidに変形してから, それらをreduceする方法が定義出来ればいいということです (p.278). 多くの場合には, foldrを実装するよりこっちを実装する方が楽という点が似ています.

H本 p.347には, Stateモナドに関するjoinの例が残っています.

runState (join (state $ ¥s -> (push 10, 1:2:s))) [0,0,0]
((), [10,1,2,0,0,0])

pushは, Int -> State [Int] Intです.

この動作について調査しましょう.

まず, joinの定義は,

join :: (Monad m) => m (m a) -> m a
join mm = do
  m <- mm
  m

joinの型を, 具体的にStateに置き換えるとどうなるか.

join :: State [Int] (State [Int] Int) -> State [Int] Int

モナドの, 値の部分がモナドになって, ネストしているような構造です. 実装によると, まず, 一番外側のモナドを剥がして, 値として格納されていたモナドを実行するものと読めます. 上の例であれば, 一番外側のモナドは, リストに対して1:2:sと, 先頭に2つ追加します. そして, その値(m)は, 10を先頭にpushして, 返り値は()です.


joinは, join mm = mm >>= idとしても実装出来ます. これって何かを意味してる?~ モナドという抽象度の一つ高い世界では, 関数も抽象的でなければならず, idという程度の低い関数は, joinすることに劣化してしまう. モナドの世界でidしたいなら¥x -> return xしないといかんよということを示唆しているように見えるが, 具体的に何なのかは分からない.