関数型プログラミングとオブジェクト指向の抜き差しならない関係について整理して考えるを眺めてゐて思ったことをつらつらと。常体と敬体が混ざってるやうな酷い駄文ですよ。
あ、題名に「失ふもの」とか書いたけどそもそも本質的に失ふものなんてないです (我ながら酷い釣りだ)。どうせどの言語もチューリング完全である限り出来ることは変はらない。言語によって違ふのは、同じ問題を解くためのプログラムの書きやすさだけ。でも、その書きやすさが「現実的にプログラムとして書けるかどうか」という問題に繋がるから、みんな言語設計やパラダイムについて語るんですよね。
副作用と IO モナドの話
純粋関数型言語マンセーの人はその純粋さ (参照透過性が保証され、副作用のある演算ができないこと) こそが利点であるとことさら強調します。それは、全てのものを抽象的概念としてモデル化する数学・論理学の問題を解くには確かに適してゐませう。しかし、純粋さがどんな問題に対しても利点として働くとは私には思はれないのです。
例へば Haskell でファイルの読み書きをしようと思ったら IO モナドとかいふ不思議な型が出てきます。命令型プログラミング言語しかやってこなかった人は大概一度は「なんやこれ」となります。で、IO モナドを理解している人が必死に「副作用を持つ操作を IO モナドといふ値に隠蔽することで純粋さが保たれるのだ」などと説明してくれるわけです。といっても一回説明されただけで理解できる人は少なく、多くの人は同じ様な説明を何度も聞いたり IO モナドを扱ふプログラムを曲がりなりにも書いてみたりしながらゆっくりと IO モナドの意義を理解してゆく。
でも命令型言語だったらファイルの読み書きなんてもっと簡単に書けるのです。C 言語だったら fopen 関数でファイルを開いて fwrite 関数で書き込んで fclose 関数で閉ぢるだけ。ただそれだけ。
何で Haskell で IO モナドをいふものをいちいち捏ね繰り回さないといけないのかといふと、それが純粋な関数型言語だから、つまり副作用を捨ててしまったから。ところが、ファイルの読み書きといふのはそれ自体が定義により副作用を持つ操作なのです。副作用が書けない言語なのに、副作用のある操作を書かなくちゃいけない。だから仕方なく副作用を IO モナドといふものにモデル化して副作用をプログラムできたことにしてゐるわけ。
オブザーバーパターンの話
オブザーバーパターンみたいなのも純粋関数型言語でそのままやらうとすると死にさうになりますよ。「監視されるオブジェクトが持ってゐるデータが変化する」とか「監視者 (オブザーバー) を登録したり削除したりする」とか副作用だらけですからね。例の記事だとこれに代はるものとして functional reactive programming (FRP) の話が出てきますけど、あれは副作用を持つ部分をライブラリや言語処理系に押し付けて隠蔽してゐるだけ。自分が書く部分のコードには副作用は無いけど、水面下ではバリバリ副作用が発生してゐるのです。
ぢゃあそのライブラリや処理系をどの言語で書くのかって話ですよね。別に同じ言語で書いてもいいだらうけど、純粋関数型言語だったら純粋さがアダになりさう。副作用を扱ふ部分はそれに長けた言語で書きたい。
マルチパラダイム言語の意義
最近「マルチパラダイム言語」ってのが流行ってるらしいぢゃないですか。ああいふのって、かういふライブラリを書く際に便利さうですよね。
別の言語でライブラリを書くと、プログラムを結合させるのが面倒なんで。C と C++ くらゐなら楽だけど、C と Java とか JNI でくっつけるの面倒だし、そもそも JavaScript みたいに別の言語と全然混ぜられない言語もあるし。なるべくライブラリも同じ言語で書きたい。
で、ライブラリ部分は副作用をバリバリ使ふコードを書き、その上で FRP するときは副作用を使はずに純粋関数型言語として書きたい。一つの言語が、「副作用を許す命令型パラダイム」と「クロージャを第一級オブジェクトとして使へる関数型パラダイム」の両方を備へてゐれば、それができる。
って考へると、副作用を許さない純粋関数型言語って微妙だなと思ったのでした。
でも、これが上手く行くには、どの部分でどのパラダイムを使ふかをプログラマがきちんと整理できないといけなさうですね。そこら辺が課題になってくるのかなぁ。