あどけない話

インターネットに関する技術的な話など

guard の動作原理を考える

リストモナドの動作原理を考える」の続きで、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であるリストを生成するために使われます。