TL;DR
- ドキュメント上、 immutable-js の Seq は lazy とされているけれど、それってミスリーディングなのではなかろうか?
- lazy という表現から期待すること
- 変数の値や関数の結果が、実際にアクセスされるまで評価されない
- 一度アクセスされた後は評価値がキャッシュされ、二回目以降のアクセスでは再評価が発生しない
- immutable-js の Seq は上記の 1 しか満たしていない
- それって lazy というより単に都度計算しているだけでは?
- とはいえ、評価値のキャッシュすると、多くの場合パフォーマンスはむしろ低下しそうなので、挙動として悪いわけではないと思う
- 毎回評価しなおしちゃうよ、ということを分かりやすいところに書いておいて欲しい
Seq の挙動の確認
- ブラウザの開発者ツールを開き、コンソールに immutable-js のコード を張り付ける
- コンソールで下記を実行して出力を確認する
var mappedSeq = Immutable.Seq([0,1,2]).map(function(num){console.log('map called'); return num * 2;});
mappedSeq.get(0); // logs 'map called'
mappedSeq.get(0); // logs 'map called' again!
Seq の挙動の評価
実は以前、上に記載した lazy から期待する 2 つの仕様を満たした配列を、 JavaScript で実装するというのを試したことがあります。
-
http://qiita.com/kimamula/items/393ce924acd5f0839876
- 配列を lazy にすることのメリットもこの記事で触れているので、興味のある方は読んでいただければ。
このとき、パフォーマンスの計測もしたのですが、結果としては、ほとんどのケースでは他の実装 (native な Array を含め) と比較したパフォーマンス上のメリットはなさそう、というものでした。
これは、評価値をキャッシュするために余計なオブジェクト、関数を生成する必要があるため、 map や filter といった処理を大量に実行したり、値へのアクセスを何度も繰り返すようなことをしない限り、コストに見合うメリットを得られないためと考えられます。
(もちろん、単に私の実装がイケてないだけな可能性もありますが。。。)
上記の経験から、 immutable-js の Seq が評価値をキャッシュしない挙動になっていることは、正しい決断だと思っています。
ただ、この挙動で lazy と言ってしまうのはミスリーディングだと思うので、分かりやすいところに説明が欲しいです。
(一応あるけどそんなに目立たない)
実際、再評価なんてされないと思い込んでいた人いませんか?いますよね?きっといるはず。
なお、評価値をキャッシュする実装も検討されている模様
issue に上がっています。