「リストモナドの動作原理を考える」の続きで、guard の動作原理を考えてみます。
guard は、リスト内包表記では、こんな感じに書けます。
[x | x <- [1,2], x < 2] → [1]
これを do で書き直すと、こうなります。
do x <- [1,2] guard (x < 2) return x
guard の定義は、Contorol.Monad の中にあって、こういう風になっています。
guard :: (MonadPlus m) => Bool -> m () guard True = return () guard False = mzero
僕には return () が何を意味するのか、さっぱり分かりませんでした。
do から >>= へ変形
上記の do を >>= へ変形するとこうなります。
[1,2] >>= (\x -> guard (x < 2) >> [x])
後で分かったことですが、2行目と3行目は、>>= ではなく、>> でつながれているのがポイントになります。
>>= の変形
>>= を concatMap へ変形してみましょう。
concatMap (\x -> guard (x < 2) >> [x]) [1,2]
そして、map 部分を適用してみます。
concat [(\x -> guard (x < 2) >> [x]) 1, (\x -> guard (x < 2) >> [x]) 2]
guard の適応
Control.Monad での定義にしたがって、guard を展開してみます。
concat [return () >> [1], mzero >> [2]]
そして、リストの return と mzero の定義に従って、さらに変形します。
concat [[()] >> [1], [] >> [2]]
結局、以下のような >> の動作原理が分かればよさそうです。
[()] >> [1] → [1] [] >> [2] → []
>>の変形
>> の定義は、web などにはほとんど書かれていないのですが、ソースから読み取るとこういうことらしいです。
m >> k = concatMap (\_ -> k) m
という訳で、変形を続けるとこうなります。
concat [concatMap (\_ -> [1]) [()], concatMap (\_ -> [2]) []]
map の部分を適用すると、こうなります。
concat [concat [[1]], concat []]
concat を適用すると、めでたく答えが出てきます。
concat [[1],[]] → [1]
まとめ
guard の定義にある return () のタプルは、要素の数が1であるリストを生成するために使われます。