リスト内包表記の舞台裏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 ()
guard p = if p then return () else mzero
この関数を理解するコツは、ゼロおよびプラスをもつモナドに対する規則、 mzero >>= f == mzero を思い出すことです。そうすると、guard 関数をモナド演算の並びの中に置くと、すべてのその guard が False である実行が強制されて、 mzero になります。これは、リスト内包表記でガード述語が述語を失敗させ、[] になる値を生む方法と同様です。

http://www.sampou.org/haskell/a-a-monads/html/monadfns.html