関数型言語で工数削減できる理由、後編
で、結局どうなのよ、という話。まあ、いい話も悪い話もあったりするのですが。
値のバグについて追跡が容易
ある変数の値がおかしいとき、一般の言語ではその値の出所(バグコードの場所)を特定することが困難になりがちです。どこで値が再設定されるのか追いきれないからです。しかし再代入不可タイプの関数型言語の場合、その値を設定した箇所は1箇所に特定できます。値がおかしいということは、その式がおかしいのか、その式に含まれている変数の何れかがおかしいのか、どちらかです。
つまり再代入不可である限り、値のバグはコードを追うだけで特定可能です。ステップ実行で追いかけないと見つけられないようなバグにはめったに出くわしません。
並行処理によるスレッドの相互干渉箇所を限定しやすい
「変数の再代入不可」であれば、一旦確定した値の参照は何度でも、たとえ同時に行っても値は同じままです。この特徴はマルチコアにおける並行処理を実現する場合に特におおきなアドバンテージになります。文法上変数の同期処理を意識する必要が無いため、値さえ用意できれば後の処理は無制限に平行起動できます。関数言語によってはこの特長を生かして、言語処理系が積極的に並行処理を起動することによって万単位のスレッドさえも破綻せずに実行できるようなものもあります。
関数の使いまわしの幅が劇的に広がる
オブジェクト指向でも適切なクラス設計・インターフェースの適用によりかなり高度な使い回しが可能ですが、関数型言語における関数の使いまわしはその比ではありません。大雑把に言えば全ての関数がジェネリックの適用対象になるようなものなので、基本的に同じアルゴリズムが適用できるなら、プログラムは同一関数+違いがある部分のみ別の関数、といった組み合わせに置き換え可能です。
コードの密度が濃くなる
関数型言語は、一般に手続き型言語に対して、1行当りに表現できるアルゴリズムの要素が多くなります。これはよしあしで、あまりに密度が高くなるためコードを一見して読むのを諦めてしまう初心者が多かったりします。実際は難しくなったわけではなく、解釈の仕方が変わってくるだけで、2週間も学習すれば読めるようになる(そして良い所が理解できる)のですが、さすがにBASICあたりと比べると敷居は高いです。ただ、C#/Javaあたりの言語と比べて難しいかというと、本質を知るために必要な学習量としてはむしろ少ないでしょう。
コードの質が高くなる
上記に関連します。動くコードが書けるようになるために、それなりの学習が必要となりますが、そのためにコードを書けるようになる段階で最低限の作法が身につけられます。関数型言語における作法の殆どはバグが無いプログラムを書くために有用な要素であるため、コードが書ける人の技術水準がある程度維持できることを期待できます。
‥‥というか、この辺は別に、きちんと教育受けてる人なら関数型言語とか関係ないんですけど。でもやっぱり、勉強している人ののコードは違うんですよねぇ。
ガベージコレクタが必要
再帰や再代入不可の特長により、プログラムが動き続ける限り無限に変数を生成していくことになります。メモリは無限にはありませんので、使い終わった変数領域はコンパイラががんばって再利用するか、ガベージコレクタなどの仕組みで回収する必要があります。
なお、再代入不可である場合、循環参照は理論上発生しえません。この場合、GCの実装は参照カウンタだけで問題ないことになります。そのためGCの挙動が予測しやすく、一般的な言語ほどGCがネックとなる問題は発生しません(その代わり常にGCにまつわる小さなオーバーヘッドを受け入れる必要があるともいえる)。