Life Goes On

まあまあだけど楽しんでる方です

Reader モナドは引数隠蔽モナド

以前にもエントリを書きましたが、Readerモナドがさっぱり分からないので、もう一度勉強してみました。
あまり深く考えずに Reader a を a -> と読み替えると、関数の型は以下のようになります。

関数 等価な関数
(>>=) (a->b) -> (b->a->c) -> a -> c
(=<<) (a->b->c) -> (b->a) -> b -> c
(>>) (a->b) -> (a->c) -> a -> c flip const
return a -> b -> a const
ap (a->b->c) -> (a->b) -> a -> c <*>
liftM (a->b) -> (c->a) -> c -> b (.)
ask a -> a id
local (a->a) -> (a->b) -> a -> b flip (.)

ちょっと確かめてみると、確かに等価であることが分かります。

Prelude Control.Monad.Reader> (+3)`liftM`(*3) $ 2
9
Prelude Control.Monad.Reader> return 5 3
5
Prelude Control.Monad.Reader> ask 4
4

つまり Reader モナドは引数隠蔽モナドだと思えばいいわけですね。本当は第一引数があるのだけれども、あたかもそれが引数に存在しないかのように記述できると。
ということは、Reader モナドを使って書いた↓の simpleR、modifyR は simple、modify みたいに書いても同じことだ。
僕にはこの方が分かりやすいな。引数を引き回したくない気持ちは確かに分からないでもないけど、そのためにモナドの枠組みで do とか書かなきゃいけないのは、ちょっと面倒な気がする。

import Control.Monad.Reader

env :: [Int]
env = [1,2,3]

simpleR :: Reader [Int] Int
simpleR = return head =<< ask

modifyR :: Reader [Int] Int
modifyR = local tail simpleR

simple :: [Int] -> Int
simple = head

modify :: [Int] -> Int
modify = simple . tail

main :: IO ()
main = do
 print $ runReader simpleR env
 print $ runReader modifyR env
 print $ simple env
 print $ modify env