読者です 読者をやめる 読者になる 読者になる

Re: Rx.js, Immutable.js について

javascript ReactiveX

Rx.js, Immutable.js について - mizchi's blog

ファイルサイズへの異論は無い。これを使わずとも解決できる問題があるというのも同意する。それでもイマイチ釈然としない。「使う必要のない」一般公理と「使いたくはない」私情の境界が曖昧に感じてしまう。仕方がないので反論を兼ねた個人の雑感を書いてみる。いわゆる、「ついカッとなってやった」に近い。

RxにせよImmutableJSにせよ、この手の標準データ構造を提供するライブラリは基盤性が強いので、全体的使うことでバイナリサイズに見合っただけの成果をもたらす系のライブラリに区分される。そのため、局所的に使いたい場合はあまり意味がない。この辺りはReactと類似してる面がある。

使うなら上から下まで、 あるいはオニオンアーキテクチャ的な例えで言うならある階層から外側のインターフェースを統一するようにしないと、さして美味しくはない。抜け出そうと思って数時間で抜け出せる程度にしか使わないのであれば、味はでてこない。全てのイベントをファーストクラスオブジェクトとして「その上に乗せる」のがRxの狙う抽象化であるし、collection操作をImmutableにすることで、破壊的操作からの解放やshallowな比較を用いることでのcollection同士の比較の高速化を企図するのがImmutableJSである。

また、多層構成の設計を組む場合、たとえばRxであれば外部からの入力をadapterのレイヤーで抽象化しきる(本来adapterでやるべき内容を別のレイヤーに漏らさずに済む)ことができるので、多層戦略を維持するのに向いている。反面、そもそもコードベースが小さかったり、設計も単純なケースだと要らぬ抽象化を増やすので、入れないほうが楽なことも多いだろう。

学習コストという単語もざっくりしすぎではないか。それはパラダイムの学習コストなのか、APIの学習コストなのかを分けて話すべき。パラダイムの学習コストが高いという問題は、学習する本人と、ライブラリによりますね以上のコメントは避けておきたい(それ以上のことを一般化していうのは難しい)。

だが、APIの学習コストが高いという問題は

  1. よく使うものだけを覚える
  2. 全部覚える
  3. IDE支援を使う

のうち、1~3の組み合わせで解決するものなので、どれを選択できるかによる。最終的には多かれ少なかれ、よく使うものを覚えることになってしまうとは思うが、そこそこ使えるようになるまでの取っ掛かりとして考えてみる。IDEに頼るのが楽だが、それをするには静的型付けを使ってIDEにコードを解析させやすくする必要があり、やっぱり静的型付けがあったほうが(IDEにとっても)嬉しいねという話になる。TypeScriptと組み合わせるとRxを書くのが楽になるのはそれが理由。Visual Studio Codeとかがあるわけだしね。JSDocをきっちり書けば補完精度も上称する類のIDEなら、AltJSまで行かずとも良いだろう。

ライブラリ固有の弁護としては、 ImmutableJSは、原則としてcollectionの提供であり、OrderedもしくはそうでないSet/MapのAPIの延長線に位置する。 RxのAPIの多さは弁護しようがないんだが、あれはLINQ to Eventsなので、非同期なイベントの集合に対してだいたいArrayに似たcollection操作(というかクエリというか)をするのがコンセプト。なのでmap/filter/flatMapあたりを覚えればだいたいなんとかなる。これに+して非同期操作なので、待ち合わせ系のメソッドを2~3覚えれば良いだろう。Promiseの連続版であるので、これも all/some/raceに相当するメソッドを覚えれば良い。これで概念としては6つ。実際にはマイナーバリエーションがあるので開始地点で10前。新しいcollection型が登場したと思えば、なんとかなる量。覚える必要のあるAPIの数だけで言えばReactと大差はない。

(以後、私がRx好きなのでRxに関する話が続く)

もっとも、RxのAPIの数は、静的型付け言語 + 世界最高峰のIDEであるVisual Studioを使ってコードを書く前提が存在するC#由来なので、動的型付け + テキストエディタで書くのが主流のJavaScript文化圏では扱いにくいのは仕方がない。 さらにシングルスレッド環境下ではSchedulerなども使うことはさしてないし、そもそもJavaScriptにはPromiseがあって非同期処理のデファクトが出来上がっているので、わざわざRxを導入しなくても「なんとかなる」 ケースが多い。故に、あまり美味しさを感じないのは事実。逆にAndroid界隈でRxJavaがウケているのは、JavaC#と近いし、Javaがマルチスレッドという以上に、Android Javaが古くPromise相当のデファクトが無いから、という話を聞く。

1画面内で紐づく20の相関の無いイベントストリームを管理するというコストにしても、それはswitch + enumでやるか、それともObservableで経路を分割するかの話でしか無いので、あまりコストになるとは思えない。私の私見だと、巨大なswitch文を管理するのは避けたいので、enumで定義した数が多い場合は、そもそも経路が分かれていると嬉しいし、イベント間の待ち合わせが起こった場合に、そういう待ち合わせを扱える仕組みのある方が嬉しい。

JavaScriptの文化は標準化されたものが上がりを迎える世界なので、「仕様化されてないデータ構造は使いたくない」という話もわかるにはわかるが、specのpolyfill以外のライブラリは全部そんなものであるので、ライブラリ固有の問題とも思えない。

(パフォーマンス的な話にも言及しておくと、PromiseしかりObservableしかり、抽象化の代償にオブジェクトを生成して引き回すモデルになるので、抽象化を捨ててもオブジェクトの生成や余計な関数コールを避けたいような、エクストリームなハイパフォーマンスなプログラミングにおいては使わないという話はあるけれど、そんなことを言うのは今更だよね……)

まとめると

  • パラダイムが難しいかは、人とライブラリの組み合わせによりますね
  • APIが多い問題は道具で緩和が可能
    • なので静的型付け + IDEがある環境では導入しやすい(TypeScript + IDEのような環境だと使いやすい)
  • 基盤性の高い道具は、全体最適な道具として使わないと美味しくないし、コードベースが小さいうちは逆に邪魔になるのは正しい
    • 抜け出すのに最低数日〜かかるようになってからが本番

みたいな辺り。 私も結論としてはそこまで変わらないんだけれども、ポジティブに捉えているか否かはあると思う。