副作用の話

最近、副作用や関数型言語についてもめているのをよく目にする。副作用と関数型言語に関する私の見解をここに示す。

処理系はソースコードを解釈し、コンピュータの入出力、つまり副作用に変換する。ほとんどのプログラミング言語は、副作用を表現するプリミティブとその組み合わせによってプログラムを記述する。副作用は実行時に生まれるものだから、「Cの関数は副作用がある」「Haskellのコードに副作用はない」といった議論は、残念ながら意味をなしていない。実行していないのに副作用が出てきたら、超自然的な力を信じざるを得ない。

副作用の扱いについてよく議論の的になる言語としてHaskellがある。Haskellが多くの手続き型言語と異なるのは、副作用を含む計算に対して、専用の型(IO)が定義されているというだけであり、「そのコードが副作用を記述できるかどうか」を区別しやすくするためのちょっとした助けに過ぎない。その区別が拡大解釈され「副作用がない」といった主張が行われるのは、Haskell使いとしてとても残念だ。

私を含む多くのHaskellerがHaskellを支持する理由は、実は「明示的に副作用を扱える」点にある。そして、副作用の扱いに優れている理由は、Haskellは関数を扱うのが得意だからなのだ。

Haskellでは、プログラムを表現するために、モナドと呼ばれる構造を用いている。その仕組みはいたって単純だ。

  • ただ値を返すだけの処理も計算(プログラム)である。
  • 前の計算の結果をもとに、次にどのような計算をするか決めることができる。

後者を表しているのが有名な演算子(>>=)である。IOについて、(>>=)はIO a -> (a -> IO b) -> IO bという型を持っている。

IO aは、aという型が結果である計算を表す。a -> IO bは、aを受け取りIO bを返す関数で、IO baに依存して決まることを意味する。モナドは変数の概念を必要としない、シンプルなプログラムの表現方法の一つである。

既にお気づきかと思うが、(>>=)は関数が第一級オブジェクトであることを前提に置いている。Haskellが関数の記述に長けているゆえに、Haskellは副作用の表現のためのツールが作りやすいのだ。

Haskell以外の関数型言語では、暗黙に副作用を埋め込むほか、一意型を用いるといった方法も使われているが、私はモナドが一番馴染みやすかった。

ほとんどのプログラミング言語は、副作用を表現することができる。しかし、これからの手続き型言語は、副作用を明示的に扱う仕組みを積極的に採用すべきだと考えている。

最後に、「関数型言語」という言葉は、言語の分類のための便宜的で曖昧な言い方であり、プログラミングパラダイムに関する議論で積極的に用いるのは推奨しない。