言語ゲーム

とあるエンジニアが嘘ばかり書く日記

Twitter: @propella

モナドとしてのリスト

-- どうやら IO 方面からモナドの意味を勉強するのは無謀だと分かったので、
-- Haskell のオブジェクトの中で一番なじみやすいリストを使ってモナドの
-- 勉強をする。
--
-- hugs で実行する方法(これを Monad.hs として保存した場合)
-- $ hugs
-- > :load Monad
-- > evenNums2 [1 .. 10]

-- >>= 演算子はリストでは concatMap の引数をひっくり返した物らしい。
-- concatMap の型は、concatMap :: (a -> [b]) -> [a] -> [b] 。つまり、
-- 引数が関数とリスト。値がリスト。引数の関数は引数を一つ取りリストを
-- 返す(ややこしい???)ここでは、偶数と奇数を判別する単純な関数を使う。

-- concatMap に渡す関数。偶数の時は [数字]、奇数の時は [] を返す
-- > evenNum 2 -- 答え [2]
evenNum :: Integer -> [Integer]
evenNum x = if even x then [x] else []

-- 偶数の数字だけ取り分ける
-- concatMap とは、リストの要素のそれぞれにある関数を適用して、
-- その答えであるリストを一まとめにして返すという物。
-- > evenNums [1 .. 10] -- 答え [2,4,6,8,10]
evenNums :: [Integer] -> [Integer]
evenNums list = concatMap evenNum list

-- モナド演算子を使った書き方。
-- > evenNums1 [1 .. 10] -- 答え [2,4,6,8,10]
evenNums1 list = list >>= evenNum

-- 式を展開して書いてみる。
-- > evenNums2 [1 .. 10] -- 答え [2,4,6,8,10]
evenNums2 list = list >>= (\x -> if even x then [x] else [])

-- do を使った書き方
-- > evenNums3 [1 .. 10] -- 答え [2,4,6,8,10]
evenNums3 list = do x <- list
                    if even x then [x] else []

-- ここで、リストにおける return は \x -> [x] な事の確認
-- > return 2 :: [Integer]

-- モナド則その1を確認しよう。
-- (return x) >>= f == f x
-- つまり要素をリストに入れてまたばらして関数を適用するわけで、
-- 結局モナドしないのと一緒の事になる。
mona1 = return 10 >>= evenNum -- 答え [10]

-- モナド則その2を確認しよう。
-- m >>= return == m
-- あるリストをばらしてもう一度リストを作るわけで、これも何もしないのと一緒
mona2 = [1 .. 10] >>= return -- 答え [1,2,3,4,5,6,7,8,9,10]

-- モナド則その3の前に、もう一つ関数を作る。
-- 5 以下の数だったら数字のリストを返し、それ以外は空。
upToFive x = if x <= 5 then [x] else []

-- ではモナド則その3。
-- (m >>= f) >>= g == m >>= (\x -> f x >>= g)

-- この例では、左辺の式は、リストから偶数を抜き出し、さらにそこから5以
-- 下を抜き出す。右辺の式は、まず偶数で5以下を抜き出す関数を作成し、そ
-- れにリストを適用する。
mona3a = ([1 .. 10] >>= evenNum)         >>= upToFive  -- 答え [2,4]
mona3b =  [1 .. 10] >>= (\x -> evenNum x >>= upToFive) -- 答え [2,4]

-- 結論。何となくやりたい事は分かるけど、モナド則にどんな利点があるの
-- かいまいち分からない。最適化に使うのだろうか。。。
--
-- 参考 : http://itpro.nikkeibp.co.jp/article/COLUMN/20061005/249933/