関数プログラミングのアプローチ (23)
関数プログラミングはプログラマを自由にする
前回まで、関数プログラミングの枠組を壊さないようにしながら、入出力をどのように記述するかという点に焦点を当てて説明をしてきました。確かに、関数プログラミングの枠組を壊さずに入出力を記述することはできましたが、それはプログラマがある種の不自由さを受け入れることで達成できるというものでした。では、なぜそのような不自由さを受け入れて関数プログラミングを行う必要があるのでしょうか?
実際には直観に反して、関数プログラミングはプログラマをより自由にするのです。
関数プログラミングは、式の評価のタイミングを自由に入れ換えることを可能にします。それは例えば、プログラマがプログラムのコードを組み換える上で制限が少なくなることを意味しています。次のプログラムを見てください。
def somefunc (txt): c0 = txt[0] c1 = txt[1] c2 = txt[2] l = len(txt) if l == 0: return g(' ') elif l == 1: return g(c0) elif l == 2: return g(c1) else: return g(c2) def g (c): return c + str(l)
非常にシンプルなプログラムで、その意図するところは明解です。しかし、これは実際にPythonプログラムとして実行するとエラーになります。式の評価のタイミングが正しくないためです。正しいPythonプログラムは次のようになります。
def somefunc (txt): def g (c): return c + str(l) l = len(txt) if l == 0: return g(' ') elif l == 1: c0 = txt[0] return g(c0) elif l == 2: c1 = txt[1] return g(c1) else: c2 = txt[2] return g(c2)
これは、Pythonが関数プログラミングを前提としていないため、評価タイミングをプログラム上で厳密に指定しなければならないためです。もし関数プログラミングを前提としているならば、最初のプログラムを受け取って、コンパイラやインタプリタが式を自動的に並べ替えて2つ目のプログラムを実行することができます。プログラマはプログラムの構造にのみ集中して、プログラムの実行について考えることを少なくすることができます。
このような変換は、ローカルに行うだけでなく、グローバルに行うこともできます。Thunkの説明を行った際に例に挙げたif関数は、グローバルな変換の例です。先に説明した時には、Thunkを利用して動的に評価のタイミングを変更しましたが、コンパイル時にインライン化などによって評価のタイミングを変更することもできます。
さらに、この変換の恩恵はプログラマだけに与えられるものではありません。コンパイラやインタプリタは、式の評価タイミングを変更する自由を得ることで、投機的に実行したり、必要になるまで実行を遅延したりすることで、より積極的に実行効率を最適化することができるようになります。