副作用 (プログラム)

プログラミングにおいて、式の評価による作用には、主たる作用とそれ以外の副作用(side effect)とがある。
式は、評価値を得ること(※関数では「引数を受け取り値を返す」と表現する）が主たる作用とされ、それ以外のコンピュータの論理的状態(ローカル環境以外の状態変数の値)を変化させる作用を副作用という。

副作用の例としては、グローバル変数や静的ローカル変数の変更、ファイルの読み書きなどのI/O実行、などがある。
一方、高水準言語における、正弦、余弦、平方根などの数学関数では、関数内でノーマルなローカル変数の変更ぐらいしか伴わないため、副作用がない。

なお、コンピュータの論理的状態(ローカル環境以外の状態)を変化させる機能、つまり副作用を起こす機能は、それ以降で得られる結果に影響を与える。
手始めに、与えられた数字を二倍して返す機能"double"があるとする。これは主たる作用しかなく、副作用のない例である。

 double: x -> 2*x
 例:
 4 <- double: 2

このような機能では次のことが成立する。
 同じ条件を与えれば必ず同じ結果が得られる
 他のいかなる機能の結果にも影響を与えない
このような性質を参照透過性という。参照透過な機能はそれ自身状態を持たないことで、副作用とは縁がない。

一方、状態を持つ機能"add"を考える。addが、機能内部からグローバル変数eを参照し、それを増加させて返すものとすれば:

 add: x -> e:e+x
 例:
 e: 1
 2 <- add:1
 2 <- e
 ...

のようになるだろう。このような機能では機能の外側の状態を変化させてしまうために、参照透過性の一つ目の仮定が崩れ、また、eを利用する他の機能の結果も変化させるので二つ目の仮定も成立しない。addは副作用を持つ機能である。

副作用を前提とするノイマン型のアーキテクチャ、つまり、大半のプログラミング言語では、addのようなグローバル変数への破壊的代入、参照渡しされた引数に対するいわゆる“破壊的操作”、そしてインスタンス(レシーバ)に対する破壊的メソッドなどがある。一方、関数型言語では原則として副作用を存在しないものとみなし、モナドなどの手法で抽象化している。

機能が副作用を持たないことの利点は、いかなる状況でも常に同じ結果が得られるために数理論理学に基づく形式的な検証ができ、状況依存でのバグの発生が抑えられ、宣言型プログラミングができるということである。反面、副作用を持たない言語設計はノイマン型アーキテクチャと反りが合わず、効率の点で不利になることが多い。また、単純な逐次処理を行う場合は状態を中心に命令的な思考をした方が扱いやすい場合がある。このためLISPやMLなどは原則として関数型ながら、副作用を許容する設計になっている。

脚注

関連項目 
 イミュータブル

プログラミング言語理論
プログラミングパラダイム