リスト内包表記の舞台裏2
以下のリスト内包表記が実際にはどう動いているのかを確認する。
[x|x<-[1..9],x<5]
内包表記をdo記法に変形
do x<-[1..9]; guard(x<5); return x
do記法を >>= 演算子に変形
[1..9]>>=(\x-> guard(x<5)>>=(\_-> return x))
guard を if に変形
[1..9]>>=(\x-> (if x<5 then return () else mzero)>>=(\_-> return x))
m>>=f を concatMap f m 、return x を [x] 、mzero を [] に変形
concatMap (\x-> concatMap (\_-> [x]) (if x<5 then [()] else [])) [1..9]
x=3のときに起こっていること
concatMap (\_->[3]) [()] [3]
x=6のときに起こっていること
concatMap (\_->[6]) [] []
guardではなくifを使う場合は、()のかわりに直接返値を書いても同じ
[1..9] >>= (\x-> if x<5 then return x else mzero)
guard :: MonadPlus m => Bool -> m ()
http://www.sampou.org/haskell/a-a-monads/html/monadfns.html
guard p = if p then return () else mzero
この関数を理解するコツは、ゼロおよびプラスをもつモナドに対する規則、 mzero >>= f == mzero を思い出すことです。そうすると、guard 関数をモナド演算の並びの中に置くと、すべてのその guard が False である実行が強制されて、 mzero になります。これは、リスト内包表記でガード述語が述語を失敗させ、[] になる値を生む方法と同様です。