先日、この記事で「参照透明性のある関数を組み合わせてプログラミングすると、出来上がったプログラムは、きれいなツリー構造になります」と書いたのですが、もうちょっと掘り下げてみようと思いました。
純粋な関数
純粋な関数とは、副作用の無い関数です。
例えば、f(x)という関数があったときに、関数の「戻り値」が、xによって一意に定まるということです。
図では、引数のxをそのまま返していますが、 x * xでも良いですし、xを関数として、x(1)とかでも良いです。
とにかく、xに与えた値が同じなのに戻り値が違ったりすると良くないです。
不純です。
カリー化
カリー化とは、関数に複数の引数があったときに、それを分解して、1引数関数に変換することを言います。
例えばこんな感じです。
f(x,y)
↓
f(x) => g(y) => x + y
謎の擬似言語を使っていますが、fという関数とfとgという2つの関数に分けているイメージです。
図にするとこんな感じです。
これは、f(x)の戻り値をg(y)という関数にしています。関数を返す関数です。
関数を返す関数を返す関数を定義することも出来ます。
これを繰り返すことで、いくつの引数の関数ででも、全て「関数を返す関数」の形に正規化することができます。
これが、カリー化です。
関数のパターン
なぜ、カリー化の話をしたかというと、カリー化することによって、関数を2のパターンにシンプルに分類できるからです。
それは、「値を返す関数」と「関数を返す関数」です。
引数に関しては、今回の話では値だろうが関数だろうが気にしません。
そして、値を返す関数は、リストの形になります。
f(x) = x
g(x) = x
g(f(x))
例えば、こんな感じで関数を適用したとすると、図のようなリストになります。
そして、関数を返す関数は、返した関数が引数を取るため、枝が2つのツリー構造となります。
かりー化の図と同じです。(^^;
言いたいこと
つまり何が言いたいかというと、純粋な関数は、巨大な一つの二分木になるということです。
ツリー構造が、システムの複雑さを押さえる上で非常に効率的な構造だということは前回書いた通りです。
グローバル変数やIOなどの純粋でない処理が混ざると、突如変な所にバイパスされたパスが発生してしまうため、余計な複雑さが生じてしまします。
これを避けるためにも、プログラムをなるべく純粋な状態に置くということは、理にかなっているのだと思います。