• 状態は難しい
    • 変数
    • 入出力
  • 難しいなら無くしてしまえばよい
    • 状態を排除
    • 参照透明性
  • 言語処理系の外側の状態はどうしようもないので
    • 一意型
    • モナド

状態は難しい

状態は難しい。

例えば変数。変数の値が自由に変更可能なら、ある変数を使用する際には、その変数を変更可能な全ての場所を考慮しなければならない。

global変数のようなスコープの広すぎる変数で混乱した、確保した領域を二重に開放するコードを書いた、といった経験は皆あるはずである。

難しいなら無くしてしまえばよい

状態の問題を解決したい。その手段の1つとして、状態を無くしてしまえばよい、という極端なものがある。

変数を無くしてしまう。外部の状態の利用、つまりファイルの操作だとかは当然禁止する。 変数を無くしてもチューリング完全の意味で計算できる範囲に影響はない1ので問題はない。

そうすると、どの関数も、引数が同じならいつ計算しても値が同じになる。 これによって、どの関数も自由にメモ化できる、その値を使わないなら計算しなくてもよい、並行/並列化しやすい、などの良い性質が得られる。 加えて、式を見ればその定義以外の場所を見なくても値が分かるので楽である。

用語

式の値がその構成要素によってのみ決まる性質を参照透明性と呼ぶ。参照透明性を壊す作用を副作用と呼ぶ。値が引数によってのみ決まりかつ副作用を持たない関数を純粋関数と呼び、参照透明性が成り立つ言語を純粋関数型言語と呼ぶ2。 ある式の値を使う直前まで評価しないという評価戦略を遅延評価と呼び3、これは参照透明性と相性が良い4

言語処理系の外側の状態はどうしようもないので

そうはいってもファイル操作すらできないとなると実用は難しい。 どうにかして利用したいが、参照透明性は美味しいので手放したくない。

解決策として、引数を毎回変えるというものがある。引数が同じなら結果が同じという性質を保ちたいのだから、外部の状態に依存する関数は引数を毎回変えればよいのである。 この策を採用し、引数を毎回変えることを強制する手段として一意型というものを使う言語にCleanがある。

別の解決策として、外部の状態に依存する関数も排除するというものがある。それが関数でなければ参照透明性は壊さない。副作用を適切に隔離しそれを合成してプログラムを作る。 この策を採用し、副作用を操作し合成する枠組みとしてモナドというものを採用した5言語にHaskellがある。

関連する読み物


  1. 結論を証拠に挙げるようではあるが、Haskell等を触ったことがあれば、問題ないことは分かるはず。詳しくは計算論を学ぼう。 

  2. 一方で関数型言語という言葉に関しては明確な定義はない。 

  3. 式を関数抽象で包むことでその評価をその関数の適用時にずらすことはできるが、それは遅延評価とは呼ばない。そもそも評価戦略でない。 

  4. 当然ながら、相性が良いだけで必須でない。 

  5. 必ずしもその枠組みとしてモナドを利用する必要はない。