言語ゲーム

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

Twitter: @propella

リストとしての haskell モナド。内包表現と do と >>=

モナドをリストとして使う事の利点の一つに、内包表現というのがあるそうです。内包表現とは、数学で集合を現すときに便利な方法、または SQL の SELECT 文に相当する物で、例えばこんな感じ。

[ (x, y) | x <- [0, 1, 2], y <- [3, 4, 5]]

これで [0, 1, 2] と [3, 4, 5] の全ての組み合わせである [(0,3),(0,4),(0,5),(1,3),(1,4),(1,5),(2,3),(2,4),(2,5)] が求まるという具合です。これは do 記法で書くとこうなります。

do {y <- [3, 4, 5]; x <- [0, 1, 2]; return (x, y)}

よく考えないと意味不明な文です。1) 何故 x と y の順序が逆になるのか? 2) 「すべての組み合わせ」というのはどうやって実現されているのか? 実はこれはさらに do をモナド演算子に変換した方が分かりやすいです。

[3, 4, 5] >>= (\y -> ([0, 1, 2] >>= \x -> return (x,y)))

リストにおける >>= は concatMap 、return は [] の事ですので、意味は Smalltalk でいう所の collect: に似ています。リストの要素を一つずつ取り出して処理を行い結果をくっつけて返します。

#(3 4 5) collect: [:y | #(0 1 2) collect: [:x | {x. y}]] "だだし、これは結果が平らにならない"

という事は内包表現の <- の数だけループが実行されていると分かります。実際には演算子の優先順位が上手く設定されているため、モナド演算子版では括弧が不要でこのように書けます。x と y が逆になる(内包表現において、先に書いたほうが内側のループ)なのは、単に気分の問題なのかな?

[3, 4, 5] >>= \y -> [0, 1, 2] >>= \x -> return (x,y)

>>= は右結合なんだ! と一瞬思ったけど、Prelude には infixl と書いてあった。でも ([3, 4, 5] >>= \y -> [0, 1, 2]) >>= \x -> return (x,y) じゃ意味通らないし(y の立場が無い)。一体どういう事?! モナド則その3 があるからどっちでも良いのかも知れないけど、気持ち悪い。。。でもなんとなく、モナドというのが「集合とその内容」のような微妙な関係を表現するのに適している気がしてきました。まだ良くわからんけど。

復習