プログラミング初心者がHaskellのモナドに雑に手を出して大やけどする
皆さんこんにちは。相も変わらず鳥居です。
さて、毎回恒例の「素人がプログラミングを雑に勉強しようとして大やけどをする」シリーズをやっていきたいと思います(そんなに恒例になってないかorz)
早速ですが、皆さんは「モナド」という言葉は聞いたことがあるでしょうか?
ぶっちゃけ筆者が「モナド」という言葉を初めて目にしたのは、persona3というゲームの「深層モナド」という隠しダンジョンの名前からでして…って、そんなことはどうでもいいか。
恐らくHaskellを勉強している、あるいは業務などで使用している方なら知っているのみならずゴリゴリに使っているのがモナドだと思われます。また、そうでなくともプログラミングを勉強している方なら一度ならず目にしているのではないでしょうか?
さてそんなモナドですが、ネットで情報を収集していると、
・モナドとは計算を表現する構造である。
・returnメソッドと>>=演算子が定義されている。
・Haskellで入出力を行うためにはIOモナドが必須
・素人は今すぐモナドを深く理解しようとするのをやめろ
といった説明がなされています。…間違っても私が手を出すべきものではないですね…orz
…とりあえず、すこーしずつモナドに手を突っ込んでは引っ込めることをやっていくことにします。
まず具体例としてwikipediaからの引用を見てみることにします.
https://ja.wikipedia.org/wiki/%E3%83%A2%E3%83%8A%E3%83%89_(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0)
data Maybe t = Just t | Nothing
上がMaybeモナドの宣言となっています。先頭にdataとついているということは代数的データ型であるということですね。…多分。
使用例を見てみると、
add :: Maybe Int -> Maybe Int -> Maybe Int
add mx my =
mx >>=(\x ->
my >>=(\y ->
return (x + y)))
見たいに使うようです。…早速わけが分かりません。
とりあえず、このadd関数は、
・引数に Maybe Int 型を二つとり
・返り値として Maybe Int型の値を返す
関数でしょうか…?うーん分からん。
などと泣き言をいうのはやめてもうちょい観察してみることにします。
・>>=演算子が存在する。
・解説を見てみると、mxとmyがNothing ではない場合に限りこの関数は成功する。
・さらに解説を見てみると、
return :: a -> Maybe a
return x =Just x
というメソッドがMaybeモナドには実装されている。
といったことがわかります。
おや?一番最初の方で書いた、
・returnメソッドと>>=演算子が定義されている。
というモナドの原則が一番目と三番目の観察結果から見て取れます。ふむ、ちょこっとだけ考える糸口が見えてきました。
returnメソッドから見ていきます。
return :: a -> Maybe a
これがreturnメソッドの宣言内容です。aという型を引数にとり、Maybe aという型にして返すメソッドらしいですね
return x = Just x;
ここでいうJust とはMaybe型を代数的データ型と見たときのコンストラクタに値しそうです。よってこのメソッドは、型aのxという値をJust xという型Maybe aの値にして返す関数となるようです。
次に>>=演算子についてみてみる…その前にHaskellの演算子について少し触れておきましょうか。
Haskellでは、演算子も関数の一つとしてみなされるようです。例えば、
1 + 2
という演算(もちろん答えは3になりますよね?)があった時、
(+) 1 2
という風に、関数っぽく書けるようです。というよりは、1+2の方が(+) 1 2の別名であると言ったほうが正しいでしょうか。
それを踏まえて>>=の宣言も見てみることにします。
(>>=)::Maybe a -> (a -> Maybe b)-> Maybe b
見てもらえばわかる通り(…大丈夫ですよね?)、(>>=)演算子はMaybe型においては
「Maybe a型の値と、「a型の値を引数にとり、Maybe b型の値を返す関数」を引数にとり、Maybe b型の関数を返す」関数であることがわかります。…頭がこんがらがってきた。
具体的な内容は、
NOthing >>= _ =Nothing
(Just x) >>= f =f x
となっています。関数型言語特有のパターンマッチ記法が出てきていますが、それはこの際無視(!)するとして、どうやらこの関数は、
・NothingというMaybe a型の値が与えられた場合にはそのままNothingを返す。(…実際には_というワイルドカードで「a型の値を引数にとり、Maybe b型の値を返す関数」を…難しくなりそうなのでこれ以上は書きません)
・Just xというMaybe a型の値と、fという「a型の値を引数にとり、Maybe b型の値を返す」関数が与えられた場合、xにfを適用した値(Maybe b型の値)が返される
演算子(関数)となっているようです。…疲れた。
さらにその上で先ほどのadd関数を見てみると
add mx my =
mx >>=(\x ->//ここでmxというMaybe Int型の値と\x以降の「Int型の値を引数にとり、Maybe Int型の値を返す関数」を引数にとり、
my >>=(\y ->//ここでmyというMaybe Int型の値と\y以降の「Int型の値を引数にとり、Maybe Int型の値を返す関数」を引数にとり、
return (x + y)))//最終的にxとyを足したものをMaybe Int型にして返す
関数となっている…もう無理。
-------------------------------------------------------------------------------------------------
今回のまとめとしましては、
・素人がモナドを深く理解しようとすると大やけどする
になるでしょうか…ぐふっ(続くかもしれない)。