流れで、いよいよオブジェクト指向に対する懐疑心が無視できないレベルに達した2017年冬。2014(「モナドについて書かれたものを読む度に自分で書いてしまいたくなる。同じ過ちを繰り返すだけだから止めた方がいいんだけど。Wadler氏の論文が埋もれているからモナドが難しいと思われるんだ。programminghttp://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf全然簡単じゃないんですけど...でも、この論文でモナドの意義と基本的な仕組みはぼんやりと見えたような気もしなくはない。その理解をここに簡単にまとめておくことで、同じ過ちを繰り返してみることにしよう。モナドを理解するためには、関数型プログラミングにおける、純粋な関数とそうでないものの区別をきちんとさせておく必要がある。それは、入力を出力に「変換」するものである。関数型プログラミングでは、この関数を組み合わせて、変換の連鎖としてアプリケーションを表現する。この関数が以下の二つの条件を満たすとき、その関数を「純粋」な関数と呼ぶ。いう同じ結果になること、そしてそれが他の入力パターンについても同様に成り立つこと。関数は入力を出力に変換するものだと説明したが、その変換以外のことが一切起こらないことこの変換以外の出来事を「副作用」と呼ぶ。オブジェクト指向と関数型の違い」で触れたように、オブジェクト指向ではシステムの構成要素であるオブジェクトの「インターフェース」にフォーカスすることによって、システムの表現を単純化し、さらには疎結合による柔軟性を獲得していた。一方で、インターフェースに現れない内部的なデータの流れは隠蔽されるため、インターフェースだけでシステムの(厳密な)状態遷移を把握することは困難になる。少ない記述で多くを実現しようとするオブジェクト指向に対して、関数型、特に純粋関数型では、原理的に関数(入出力の変換)として書かれたことしか起こらないので、不測の事態が起こりづらく、より信頼性の高いシステムを構築しやすいと言える。逆に言えば、全てのデータの流れを入出力の変換として明示的に表現しなければならないので、時にはコードが冗長になってしまうケースがあるだろう。この関数型の欠点とも呼べる冗長さを軽減するために導入されたのが「モナド」と呼ばれるコンセプトである。以下のような例で考えてみる。ここに「倍返し」という関数がある。名前の通り、受け取った数値を倍にして返す関数である。この関数が「受け取った数値を倍にする」ことだけに専念しているとき、この関数は純粋であると言える。世知辛い現実世界では、単に「倍返し」の純潔を守っているだけでは生き残って行けない。色んな要求が舞い込んでくる。倍返しに失敗したらどうしてくれるんだ?、だったり、倍返ししたことをちゃんと記録しておけよ、等々。そこで以下のような要求があった場合、どのように対応すれば良いだろうか?エラーが発生したら、それをきちんと知らせて欲しい何回「倍返し」したのか記録しておいて欲しい倍返しする度にログを吐いて欲しいもし、関数が純粋でなくても良いなら、話は簡単である。エラー処理については、出力以外にも「例外」という経路を設けて、そこからエラーを知らせればよい。実際に多くのプログラミング言語ではこの方式を採用している。倍返し回数の記録についてはグローバルな変数を用意してそれをカウントアップして行けばよいし、ログについては深く考えることなく、普通にログを吐けばいい。単純に考えると、出力にそのような情報を追加するしかない。ログエラーを投げたり、回数を記録したり、ログを吐いたりするのは「副作用」に当たるので、関数の純粋性を守るためには、これらの処理は「予定」として、関数の出力に含めて表現することになる。これは明確さという観点から言うととても分かりやすい。関数の定義を見ればどのようなことが起こるのかが一目瞭然だからである。一方で、関数の定義に、本来の処理「倍返し」以外の情報が紛れ込んでくるし、要求が増える度にいちいちこのような関数のバリエーションを増やして行かなければならないのだろうか?そこで、このような「入出力に紛れ込む副作用」を本来の処理から隠蔽して冗長さを軽減するために導入されたのが、いわゆる「モナド」である。数値として扱えれば便利である。そのために必要となるのが以下の仕組みである。本来の入力この三つの組み合わせを「モナド」と呼ぶ。これらの道具立てがあれば、関数本来の計算がおまけに埋没することなく、かつ関数の純粋さを保つことができるというわけである。[2017/05/18追記]久しぶりにこの記事を読んでみて、もう少し説明を加えればモナドに対する理解がよりクリアになるような気がしたので、試しに書き加えてみる。関数型プログラミング(特に純粋関数型)は、システムの状態遷移を余すところなく関数の入出力として表現することで、システムの動きを明確にしようとする。全てを入出力として表現するので、ビジネスロジックにとって重要な「本来の入出力」以外の「おまけの入出力」がどうしても必要になってくる。関数と関数の間を受け渡されていくデータには、常に何らかのおまけが含まれている可能性があるということだ。そのおまけは、例えば、処理に失敗した際のエラー情報だったり、値が存在しないかもしれないという可能性だったり、あるいは入出力には現れない文脈的なデータだったりする。このおまけの応用範囲がとても広いというのが、モナドのつかみどころのなさの一因になっていることは間違いなさそうだ。関数の方はこのおまけを解釈するようにはできていないかもしれない、あるいはむしろ、特定のおまけを想定して関数を書いてしまうと、その関数の汎用性が大きく損なわれてしまうので、できれば本来必要な計算だけに集中したい。そこで、おまけを想定しない関数を、おまけ付きの入出力を持つように変換するアダプター的な仕組みがあれば、本来の計算とおまけを切り離せる。この仕組みがモナドである。正確に言うと、この変換には三つのタイプがあり、その中の一つがモナドと呼ばれている。おまけのように扱えるようにしたい。(アプリカティブ)と呼ぶ。theory)」という分野由来の言葉で、そのままで意味をイメージできる人はかなりの少数派ではないかと思う。この名前の問題も、モナドが難しいというイメージを与える一因になっている。そこで、以下のようにもっと分かりやすい名前で呼ぶようにしたらどうかという提案がなされているようだ。Zippable厳密にはこれらの語が意味するところとは異なるので(圏論的なニュアンスが抜け落ちるので)適切ではない、というのが専門家の見解であるようだが、これらの概念を理解する際の参考になることは間違いない。いう別名はとても分かりやすい。