関数プログラミングのアプローチ (10)

入出力が関数的でない理由

前回から新しいテーマとして、「行番号を付加するプログラム」について検討することにしました。このプログラムは、テキストファイルから内容を1行ずつ読み込んで、行番号を付加して別のファイルに出力するというプログラムです。まず最初に、前回の命令的なプログラムが関数的ではない点がどこかを説明します。
関数的なプログラムとは、参照透明性が成り立っているプログラムであるということを以前説明しました。参照透明なプログラムは、

  • 同じ引数に適用した関数は、同じ結果を返す
  • 評価順序を変えても結果が変わらない

という点が成立するはずです。次のプログラムを見てください。

ofile = open('output.txt','w')
ofile.write('1行目\n')
ofile.write('2行目\n')
ofile.close()

プログラムの評価順序を変えた次のプログラムとは、明らかに結果が異なります。

ofile = open('output.txt','w')
ofile.write('2行目\n')
ofile.write('1行目\n')
ofile.close()

また、同じ引数に適用した関数が異なる結果を返すという例としては、次のようなものがあります。

def readfrom (fname):
    ifile = open(fname,'r')
    r = ifile.readline()
    ifile.close()
    return r

r1 = readfrom('file.txt')

ofile = open('file.txt','w')
ofile.write('over-write')
ofile.close()

r2 = readfrom('file.txt')

r1とr2は全く同じ引数に全く同じ関数を適用しているのに、値が異なる可能性があります。
一般に、入出力を伴うプログラムは、プログラム中の式の評価のタイミングが、式の実行結果に決定的な影響を与えます。しかし関数的なプログラムは、式の字面が同じならば、式の評価のタイミングや回数にかかわらず、式の実行結果は全く同じにならなければなりません。そのため、関数プログラミングにおいて入出力を行うには、特別な取り扱いが必要になります。