参照透過性を考慮する

PHPでこんなことをやるのは邪道かもしれないが、オレオレFWの設計指針には関数型ライクな考え方を盛り込みたい。成果物の評価法は開発工数と機能性に偏りがちだけれども、自前のサービスに利用する場合なら、内面的なクオリティを高めておくことは意味があると思う。

参照透過性*1

ここでの参照透過性はごくごくニュアンスのみで、厳密に参照透過性を追求するというよりも、実装に迷ったときは、透過性の高い方を選ぶというイメージ。そのための指針。厳密に参照透過性を実現しようと思うなら、関数型言語で実装すればよいわけでわざわざ対極にあるPHPを使う理由がない。しかし、PHPの利便性を生かしつつ関数型言語のおいしいところをいただきたい。

  • 入出力を体系化し、入力値と出力値の間の依存関係について正確に把握する。
    • 入力
      • リクエスト、モデル、継続実装用セッション、イレギュラー
      • php関数やフレームワークに混入するその他データ
      • コンフィグ
    • 出力
      • クライアントに送信されるソース、セッション、継続用データ
      • メール、ログ、キャッシュ、
  • 「参照透過性を壊す操作」を、「維持できる操作」とできるだけ分離・区別する。

参照透過性そのものが直接メリットを提供するわけではないが、システム内での参照透過性を高めておくことは、クリーンな実装を行うための基礎になると思う。現実的には、参照透過性をシステム全体で維持するというよりも、疑似的に維持しているように見せかけるセグメントを作成することになる。
たとえば、部分評価を応用したキャッシュ機構としては、下記の論文がある。
http://ci.nii.ac.jp/naid/110002726555/
簡易的には、
http://d.hatena.ne.jp/noopable/20090216/1234739892
のような実現方法があるが、泥臭い方法なので、開発者間で変更に関する高度な理解を必要とする。

メタファー

単純なMVCの例

<?php
class model extends ArrayObject {}
class view extends ArrayObject {
    public function __toString()
    {
        return join('', $this->getArrayCopy());
    }
}
class controller {
    public static function process($input)
    {
        $model = new model($input);
        $view = new view($model);
        return $view;
    }
}
$input = array('test', 'test1');
$output = controller::process($input);
//printは分類上、関数ではないが、ここでは副作用を伴う関数とみなします。
//printの副作用がoutputの本質です。
$result = print($output);

このプログラムで定義しているのは、コントローラーを一つの関数と見ると、
f(input) → output
になるということ。
それで、このfを分解すると
fv(fm(input)) → output
fv fmは引数をとってインスタンスを返す関数と考えます。
でも、fm(fv(input))≠fv(fm(input))なんですけど。

具体的な違いなどは今後試行錯誤していくとして、実装方法に迷った時は関数的に明確な方を選択していきたいと思います。

たとえば

参照透過性があると人間が断定すれば、上記のプログラムはもっと簡略化できる

<?php
function process($input)
{
 return join('', $input);
}
$input = array('test', 'test1');
$output = process($input);
$result = print($output);

このようにしてみると、controller::processはfunction processと等価であると、断定できる。
どうみてもこっちの方が速い。

*1:本来の関数型言語での定義とは異なります。あしからず