2015年3月11日水曜日

副作用が無いとはどういうことか

関数型言語の説明の中には、
副作用が無かったり、あったとしても限定的だったりという話が出てきます。


しかしそれだけ言われてもどういうことかイマイチ理解できません。

JavaScriptは引数と戻り値にfunctionを指定できるという意味で関数型っぽいですが、
副作用の有無では不合格のため(関数型言語から見れば)サブルーチン扱いされているようです。
Qiitaの記事のコメントより)

ちょっと原典が無いので間違っていたりする可能性大ですけど、
実際のコードで比較してみようかと思います。
(間違っていたら指摘をお願いします。)

まずはJavaScriptで副作用がありそうなコードを書いてみます。
function foo(x){ var y = x; y.push(5); return y; };
var bar = [1, 2, 3];
foo(bar);
foo(bar);
foo(bar);

配列に要素を追加したものを返す関数を3回実行しました。
変数barの中身は、
[1, 2, 3, 5, 5, 5]
になるかと思います。

もしも変数の中身を変えずに同じ機能を提供するのであれば、
function foo(x){ var y = x.slice(); y.push(5); return y; };
と明示的に配列をコピーする必要があります。


次にSchemaで同じ(と思われる)コードを書いてみます。※色付けが変なのは許して!
(define foo (lambda (x) (append x '(5))) )
(define bar '(1 2 3))
(foo bar)
(foo bar)
(foo bar)
BiwaSchemeでテストしました。)

同じような関数を3回実行しましたが、
変数barの中身は、
'(1 2 3)
のままです。
(defineではなくletで試しても同じはず)

もしもbarを書き換えたいなら明示的にそれを示す必要があって、
(set! bar (foo bar))
のようにset!という関数を使う必要があります。
(Schemeでは副作用がある関数には!が付与されているそうです。)


関数がどうこうって言うより変数は副作用の塊みたいな話ですが、
まあ実際その通りなわけです。

オブジェクト指向言語と比較される理由はこのあたりにあって、
メンバ変数はすなわちステートフルな副作用の塊と言えます。

でもそれは反対の性質を持っているというわけではなくて、
扱っている問題の領域が違うだけなのです。

オブジェクト指向でも副作用を極小化したメソッドは可読性が高いものです。

その部分がλ式に代えられ、
副作用が無くなる(かもしれない)ことに何の問題がありましょうか。

0 件のコメント:

コメントを投稿