それについて考える良い方法は、「私が返す値以外に、後のコード (この同じ関数を後で再度実行することを含む) が見る可能性のあるものを変更したか?」ということです。もしそうなら、それは副作用です。そうでない場合は、存在しないことがわかります。
したがって、次のようなものです:
let inc_nosf v = v+1
整数 v より 1 大きい新しい値を返すだけなので、副作用はありません。したがって、ocaml トップレベルで次のコードを実行すると、対応する結果が得られます。
# let x = 5;;
val x : int = 5
# inc_nosf x;;
- : int = 6
# x;;
- : int = 5
ご覧のとおり、x の値は変化しませんでした。したがって、戻り値を保存しなかったため、実際には何もインクリメントされませんでした。関数自体は戻り値を変更するだけで、x 自体は変更しません。したがって、それを x に保存するには、次のようにする必要があります。
# let x = inc_nosf x;;
val x : int = 6
# x;;
- : int = 6
inc_nosf 関数には副作用がないため (つまり、他の変更を行うのではなく、戻り値を使用して外部の世界と通信するだけです)。
しかし、次のようなもの:
let inc_sf r = r := !r+1
r で表される参照に格納されている値を変更するため、副作用があります。したがって、トップ レベルで同様のコードを実行すると、代わりに次のようになります。
# let y = ref 5;;
val y : int ref = {contents = 5}
# inc_sf y;;
- : unit = ()
# y;;
- : int ref = {contents = 6}
したがって、この場合、戻り値をまだ保存していませんが、とにかくインクリメントされています。つまり、戻り値以外に変更があったに違いありません。この場合、その変更は:=
、ref の保存された値を変更する代入でした。
経験則として、Ocaml では、参照、レコード、クラス、文字列、配列、およびハッシュ テーブルを使用しない場合、副作用のリスクを回避できます。ただし、String.set や String.fill などの関数を使用して文字列をその場で変更しない限り、文字列リテラルを安全に使用できます。基本的に、データ型をその場で変更できる関数は、副作用を引き起こします。