普通のアプリケーションのコードに関数定義と関数適用とリテラル以外を書いてはいけない
underscore-fix とか Pastaとかを仕事で使ってみていたら、前から薄々感じていた事に確信を持った。
ライブラリが十分に強力なら、プログラム中に関数定義、関数適用とリテラル以外は出てこない。そして逆に、forやifやswitchやnewが必要になったら、その関数は一般化してライブラリに押しやらなくてはいけない。
On Lispだかなんだかのボトムアップデザインの説明で、「アプリケーションを書くための言語を作って、その言語でアプリケーションを書く」みたいな文章を読んだ記憶があるけど、この「言語」っていうのは、別にDSLである必要もLispマクロである必要もなくて、「ライブラリ」って意味と取って問題なくて、それならLisp以外の言語でもこの考え方は重要になる。自分のライブラリが十分に成熟するまでは、なにかアプリケーションを作る度にライブラリ関数を増やすつもりでやらなくちゃいけない。
理想的には、8~9割の機能は抽象化(一般化)されてライブラリに格納されて、残りの1~2割が、ライブラリ関数を組み合わせて作るそのアプリケーション専用のコードってことになるんだと思う。
フレームワークがユーザ(プログラマ)にクラスを定義させたり、ライブラリがクラスで提供されていてユーザがnewしなきゃいけないとか、よくあるけど、これは実装詳細が漏れている間違ったツールだと思う。ユーザが必要なのはフレームワークではなく「プログラムを作る関数」だし、ライブラリのユーザへのインターフェースははクラスではなく、関数を提供するべきだから。(Stop Writing Classes)
まとめると、
- ifやforやclassやnewはライブラリ作者専用の機能
- ライブラリ作者はアプリケーションプログラマにifやforやclassやnewを書かせてはいけない
- アプリケーションプログラマは、ifやforやclassやnewを使っているコードをアプリケーション本体のコードから分離しなければいけない
こんな感じですか。
一言で言ってしまえば「抽象を分厚く具象を薄く」ってことです。
考えるきっかけになれば幸いです。ではでは。
[追記]
再帰も書いてはいけないと明示した方がよいのではとの指摘を頂きました。全くその通りです。コントロールストラクチャは全て汎化した関数を作ってライブラリに入れるべきだと考えているので、再帰は関数適用だからOKということはありません。リストの場合なら再帰よりfoldを, foldよりmapやfilterを, mapやfilterをin-placeで書くより宣言的な関数を作るとよいでしょう。
[さらに追記]
ifやforやnewと文章中に書きましたが、これはあくまでもアプリケーション固有のコードを宣言的に書くのに絶対に必要というわけではない構成要素の例として挙げたものです。ifやnewやforが無い言語であってもアプリケーション固有のコードには関数定義、関数適用、値(イミュータブルなもの)くらいの最小構成要素のみを使うべきという立場は変わりません。逆に、関数が無いか使いづらい純粋OO系言語でメソッドオブジェクトやブロックを使って関数をエミュレートするというのは馬鹿らしいのでそこは臨機応変に最小構成要素を探し出す必要があるでしょう。ただそもそも宣言的なコードを書くのに向いていない言語(やパラダイム)ももちろんあって(SP,OOPなど)、そういう言語は僕は使いたくないです。