プログラミングパラダイムとは制約をかけることだという見方

ふと感じたのだが、プログラミングパラダイムというのは、プログラムに制約をかけることなのではないか。

基本的に、現代で書かれるプログラムはどれも非常に長く複雑で、人間の脳みそに収まりきらない。実行パスをトレースできない規模のものはザラにあるし、長さがそもそも覚えられないものもある。

このようなプログラムを頭から完全に理解するのは不可能だ。大規模なプログラムを頭から完全に理解するというのは単に文字列として覚えるという意味ではなく(そもそもそれが不可能だと思うが)、様々な入力や内部状態に応じてどの実行パスを通りどの処理が実行されて、それに伴ってどの程度のメモリが消費され、どのスレッドがどの領域のMutexを取得して……。プログラムを文章として読むだけではなく、頭の中でその挙動をエミュレーションできる必要があり、さらに爆発的に増えていく状態や入力の組み合わせの全てに対して実行してみる必要があるのだ(頭の中で)。そもそもそのようなことをしようとすることが時間の無駄のように思える。だいたい、活発に開発されているプログラムは同時並行で異なる部分が編集されていったりする。すると知ったそばから内容が変わっていたりする。こうなると理解するのはもう不可能だ。

なのにどうやってプログラムは開発されているのだろう。理解できていないものを適当に変更したらたいていの場合壊れるのだが、壊れずに動いているプログラムが多いのは何故だろう。それは、主だったプログラミングパラダイムや積み上げられてきた知見がどれも、組み合わせ爆発を抑えていくようなものになっているからだ、とふと思ったのが今回のこの記事だ。

オブジェクト指向的な考え方が何かというと、大雑把には、単一の仕事に対して責任を持つクラスを作り、それに仕事をさせるためのインターフェースを設計し、そのクラスはその仕事を完璧にこなし、外に内輪の情報は何も漏らさないというものだろう。これは、単一の仕事についてそれをこなすために必要な処理を全てブラックボックスに閉じ込めることで、プログラム全体の状態数を減らすという戦略だと言えなくもない。

関数型はもっとわかりやすく、状態が作られることがないので、その部分の複雑さは一切ない。その瞬間にその関数に渡ってきた入力が全てなので、見るべきものが非常に小さくなっていることになる。

このような観点から見ると上の2つは同じことだ。単一の仕事について、その処理をまとめ、それに付随する内部状態が全体に波及しないようにする。簡単な話じゃあないか。

特定の部分について考えているときに他の部分については考えずに済むなら、頭から爪先まで理解している必要はない。読んでいる部分に集中すればよい。

そう考えてみると、goto-lessプログラミングもそのような見方で捉えられなくもない気がする。というのも、gotoがあるプログラムは今読んでいる行に突然全く別の内部状態で処理が遷移してくるかもしれないと怯えながら読まなければならないので、これはつまりプログラムの全状態の組み合わせを全ての行で考慮しなければならないことを意味する(これは若干誇張しているが)。なので、この強すぎる武器gotoを使わないようにする、または使う場面を非常に強く制限することで、考慮すべき状態数を減らしたのがgoto-lessプログラミングだと言えるだろう。

これらは人間の小さな脳みそが組み合わせ爆発に対抗する手段だ。そういう発想があれば、どのパラダイムも納得がいくようになるのではないか。