RxJSとRxJavaとでは旨味のコクが違うように思う

Rx(もしくはReactive ExtensionsないしReactiveX)には

  • Observable
  • Operators
  • Subject
  • Scheduler

と云った概念がある(分類はReactiveX.io調べ)。

で、Rxを用いた非同期処理を実際に行うにあたっての実行計画を制御するための要素が Scheduler に当たり、実行タイミングや実行スレッドの制御をこれで行うことでRxはLINQ to Events足り得ており、時間軸に関係するイベントもテスタブルにしている……みたいな話はRxをかじった人なら知ってると思います。かじってない人はそんなもんだと流してください。

で、ここしばらくRxJSでいろいろやっていたのだが、このSchedulerの重みが、JavaC#とは全く違うのであろうという仮説を抱くに至った。

メモリ空間共有型のスレッドモデルを持つJavaC#に於いては、非同期処理・並行処理を行うにあたってはスレッドを適切に掌握する必要が有る。で、RxはSchedulerがあるおかげで、この面倒臭いスレッドの概念もそれなりに統合して取り扱えるようになっている、というのが売り文句の一つ。

一方のJavaScriptにはメモリ空間を共有するようなスレッド機能はなく、あるのはshared nothingなWorkerであり、基本はシングルイベントループをぶん回し続けるモデルになっている。そして、多くの処理は(特にNode.js方面のAPIは)非同期を前提としたAPIモデルとなっている。

故に、RxJSが提供するSchedulerは、同一スレッド上での実行タイミングの操作とテストダブルであり、スレッドをまたぐ処理への抽象化を加えるようなインパクトは無い。

なので、JavaScriptにはPromiseもあるしSchedulerのインパクトも無いし、JavaC#の場合に比して「冒険してまで導入したい」成分は薄れているように思った。人間、華々しさや煌びやかな様子に負けて手を出すのは常なので、インパクトが無いと起爆剤が欠けるよなあ。

breaking changeの整理

生きているソフトウェアに付き物の破壊的変更(breaking change)であるが、一口に破壊的変更と言っても、実際は複数の意味を包含している。 なお、「生きているソフトウェア」では主語が大きすぎるので、ライブラリであったりランタイムについての話とする。

一般的に破壊的変更という場合、以下のどちらかとなるだろう。

  1. セマンティクス・挙動の変更を伴う変更
  2. セマンティクスの変更を伴わないが破壊的とみなされる変更
    1. APIの名前が単純置換された

1は明らかに破壊的である。ソフトウェアに対して少なからぬ書き直しが発生するし、単純置換では済まないケースの方が多い。 そもそもの設計思想やアプローチに修正が加えられたので、前と後とでは違うものと言っても過言ではなく、十分に破壊的と呼ぶに足りえる。

しかし、2のケースは一概に破壊的と言えるかは怪しい。辞書的には破壊的だが、我々の思い浮かべる大規模な書き直しを強いられるような破壊性が あるわけではないからだ。

2を破壊的と呼ぶか否かは、対象となる環境の置かれたバックボーンに依存することとなる。例えばRustのような強い静的型付の言語では、 コンパイル時に静的解析が発生するため、APIが消えただの名前が変わっただのは只のコンパイルエラーとして処理される。 標準ライブラリに絡む変更である場合はコンパイラが警告を細かく出してくれることもあるだろう。この場合、破壊的なリスクは早期に発見できるので、 相対的に破壊的変更は軽度の問題と化す。

逆に、実行するまでわからないような言語環境(典型的にはJavaScriptが該当する)では、このリスクは格段に跳ね上がる。 動かさないとわからない上に、実際にそのパスに入って実行されないわからない変更である場合、「やってみるまではわからない」ブラックボックスと化す。 エラーで落ちれば御の字だが、実は上方のtry-catchがエラーを握りつぶしてしまっており、前提条件が崩れたままに後続の処理に入り……などいう袋小路に入ると本当にどうしようもない。 この場合の破壊的変更のリスクはセマンティクスの変更と差はない。なので、我々はこれを早期に発見できるように頑張る必要がある。 semantic versioningは絶対遵守されるべきである。道徳律を超えた規範にして、これに逆らうライブラリ作者は異端審問で火刑に処されても構わない…というのは、やりすぎだが、 こうした自身のソフトウェアの破壊的な挙動につながる事態は想定されるべきとなるし、それを避けるためにテストコードの充実やカバレッジといった指標の整備、 CHANGELOG.mdを記述する道徳律の作成コミュニティの規範と正義を整えて共同体秩序を守る必要がある。かなり面倒くさい。できればあんまりやりたくない。

ここらへんの文化圏の交差とか色々残りはあるんだけど、書いてる最中にやる気がなくなったので寝ることにする。おやすみなさい。

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

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のような環境だと使いやすい)
  • 基盤性の高い道具は、全体最適な道具として使わないと美味しくないし、コードベースが小さいうちは逆に邪魔になるのは正しい
    • 抜け出すのに最低数日〜かかるようになってからが本番

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

銀の弾丸は川から流れて来ない

The Evolution of Flux Frameworks — Mediumを読んだ。自分がここ3ヶ月~半年くらい考えてたことと殆ど一緒で、若干の安心感を得たり。実装論の話も概ね同意ではあるのだけれど、自分は必ずしも同意しかねる面がある。

今のメモリマネージド言語のトレンド、特にクライアントアプリケーションの存在を想定したアーキテクチャは、C#が筋道を立てた実践理論に追随してる面があるので、過程はどうあれ、彼らの先端スタイルに近づいていくことになると思うのよね。

Fluxパターンの話をすると、あれが偉かったのは「疎結合すると色々楽になるから、オブザーバーパターンにして、コマンドパターン使って、コマンドは単方向にすると破綻しにくい割に弄りやすいよ」ってのを、フレームワーク症候群に陥っていた凡百なWebクライアントサイドに、一発、投げつけた辺り(というのは半年くらい前に書いたな……)。

だけど、彼らのサンプル(chat/todo)は、サンプルとしては綺麗ながらも、domain modelに相当する物を必要としないアプリだった。ToDoアプリはviewのstate modelとdomain modelが概ね一致するし、chatアプリも極端に簡素化するとクライアント側にdomain modelなんて物は要らず、viewのstate modelだけで完結してしまう。故にdomain modelをどこに置くべきかという論争が起きてしまう。Fluxは設計指針に過ぎず。そこに出てくる用語類は所詮はview領域の話であり、DDD的な文脈(奈須きのこではない)で頻出するオニオン(layered)アーキテクチャの外側を彷徨うかのような物であった。故に、決定打としてのフレームワーク実装は存在せず、多くのプロジェクトが自身のパターンのベストプラクティス体験談をフレームワークとしてスピンアウトしている(そのようにスピンアウトした方が其々にとっても都合が良い)。実際にスピンアウトするかはともかく、リポジトリ内にローカル流派が出来るのはよくあることでしかない。いずれにせよ、Fluxという概念は、「指針は語れども全体への実践は語れず」と私は結論することにしている。故にアーキテクチャ未満のパターンと呼びたいし、そこには欠けたピースが幾つもある。

domain modelの有り様は、アプリケーションの目的によって違うので、どこらへんに・どのように置くかという問題は個別事例となる。奈須きのこではないDDDによくあるとされる設計パターンもプログラミング技術の側面であり、万能の解法と呼ぶべきベストプラクティスと捉えるのは短絡的に過ぎるであろう。だが、間違い無くview(人間という外界とのinterface)からは遠くに位置することになるであろう。また、domain modelを本気で造ると、viewの状態モデルとは別個に別れることになる。これはview固有の状態や入力値バリデーションの取り扱いが必要になるのケースが、ままあるから。逆説的には、viewを反映するview state modelが必要になり、これは即ちMVVMじゃねという趣が出てくる。このvisual stateをobserverでviewと繋げばdata bindingになる。歴史が繰り返して来たぞ!

この繰り返す歴史で、前回(例えばAngularJSのような)と異なる点は、機械生成や実行時生成に基づくdata bindingではなく、静的に人間が読み取れるコードによってdata bindingが為されている、explicitにデータの経路が露出している点に有るだろう。そして、これを支えるのがReactive Extensionsなどになる、というのが、私の現在の認識と趣味嗜好さね。

in browserで同種のことを行おうとした場合の、この単方向の経路の作成時に、細かいパスを作らずに巨大なview state modelを投げつけるだけでも良いように、性能劣化を有耶無耶にしたのがReactのVirtual DOMであるし、故にあれは割と置き換え可能な部材である、というのは再確認でしかないね。Rxも、従来も頑張れば出来なくは無かったが、異常に労力がかかっていた・手離れの悪い実装になっていたものを、抽象化を用いて少し簡単にしたというだけに過ぎないわけで(そして、その簡単にした程度が著しく大きかった)。

Reactブームとか、これからクライアント側JavaScriptにやってくることを思えば道半ばですよ奥さん! 結局はC#の連中が取り組んだ事のアレンジですよ全く!

RxJSをもくもくしてReactivePropertyの価値らしきものを気づかされた話(仮)

Reactive Extensions JS port(RxJS)をもくもくしていた結果、C#界隈がReactive Propertyを作り出した理由が(なんとなく)わかってきた。

Rxの流儀にのっとれば、ある値を使おうとする場合、その値を生成するObservableの結果を受けて、そのObservableも同様に……とObservableの依存関係を作っていく必要がある。

Rx.Observable.do()メソッド中に、関数外のグローバル変数などを副作用なmutateをさせてもいいが、これは基本的によろしくない。Rxで実行される各Observerが、一体どのタイミングが動作するかの順序保証が本来的には存在しないためだ。イベントループがひとつしかない & Workerは完全にメモリ空間が分割されたアクターライク & Rx.SchedulerもDOMのイベントループ原則に則るJavaScript portではそこまで極端な問題にはならないかもしれないが、その他多くのメモリ空間共有型のスレッドを用いる種類の言語では容易にdata raceなどの問題を抱えることとなるように感ずる。

この問題を避けるには依存関係にある値のObservableを愚直に紡いでいけば良いのだが、従来の手続き型プログラミングに慣れた我々には慣れない面があるし、明らかに冗長とも思えるケースがある。突如として飛び道具的に副作用を起こして問題を解決したい場合もある。行儀がいいとは言えないが、GUIプログラミングではこういう需要はままある気がするし、そもそも言語が副作用の存在を許容している以上、その帳尻を合わせる為にどこかで泥を被る必要がある。

こうした問題に対してC#の人々が考えついたアプローチがReactivePropertyであるということに気づいた(名前だけは知っていた)。値そのものをRx.Observableに変換できるコンテナを用意し、可逆的な変換も可能なコンテナ。値の変更が起これば、そのイベントをObserverへと伝播させる。

値のmutateは副作用であるが、Observableのチェーンをつなぐことで依存関係を表明することができる。サンプルコードを見るだけでは結構危なっかしいし、GCあり言語だなと感じる面もある。が、値のmutationに伴って、イベントが常に流れ続け、それが最終的にatomicに値を上書きするのだと気付くと、妥当感にあふれる。いい意味で言語的クリーンさを無視している。良い。

さっそくRxJSでも使ってみようと思ったが、同名かつNode.jsのEventEmmitterを使ったライブラリは何点か見つかるが、Rxベースのものはない。これはライブラリ作成のチャンスかもね(本業でRxを実戦投入するには必要と思われるので、その時は自分でなんとかするかもしれない)。

しかしこうして見ると、言語に後付けて導入したモデルとしては、Reactive Extensionsは本当に良い塩梅で出来ている。もちろん私が地雷を踏んでいない可能性もある。API designがVisual StudioのIntellisenseによる補完が明らかに前提となっていて、IDEの支援無しに書くには二の足を踏むのは否定できない(Visual Studio + TypeScriptで解決するとは思うけど)。ただ、これを見た後にES7 Object.observeをみると、Promiseなど以前のパラダイムに生きているなんとも中途半端感を感じずにはいられない。

「オメー何言ってるんだ、それは違う」というC#系の方がいらっしゃいましたらツッコミをお願いします

makeのくびき

「gulpって何だよ、makeでいいじゃん(要約」論争について、私もちょっと一本講釈をぶってみることにする。あれやこれやといった実利的な話をするつもりはない。そういうものは既に書いた人がいるのでそちらを参照のこと。

Gruntの思い出

Gruntは、私の印象で言えば車輪の再発明の失敗作のようなもので、タスク間の依存関係が破滅への一途をたどり管理不能に至るなど、宣言型の負の側面が強く出てしまった。しかし、設定は本当にサンプルコードのコピペだけで組み立てられるので、JSが不得手なデザイナーなどには非常に受けが良かったという点は忘れてはならない。ちょうど、html5ブームが本格化して, Apache Antとかに慣れ親しんだJava(主にSIer)系の人が入ってきたタイミングにあった道具かつ、Yeomanファミリーにも組み込まれており、それでいて簡単な事をやらせるには悪くはない具合のシンプルさ、そして設定ファイルがホスト言語で書けるという、「悪くない」塩梅だったために流行ったというのが私の感想。あ、ファイル監視によるタスクの自動実行機能いわゆるwatchも流行った一因だろう。ただ、まあ、GruntであればMakefileを書くのと大差ないと言われても仕方が無い、むしろmakeの方がまだマシだと言われても仕方が無いようには思う。私もそこについては割と同意(というか、私はGruntfile書くなら俺はMakefile書くぜ!な人間だったので、まんまそういう意見だった)。

gulpたん

Gruntがそこそこ使われるようになってきて、「これって色々やり始めるとイマイチじゃね?」という認識がアーリーアダプターの間で醸成され始めた矢先に出たのがgulpだったように思う。これは中々にmodernな着想に立っており「ビルドプロセスとは、ファイルストリームに対してtransform関数を適用してmapを行うものだ」という前提に立っている(意訳)。パイプラインの各タスクの単位を明確にmap関数に切り分けたので、処理パイプラインJavaScriptのfunctionとして明瞭になった上、streamとしてオンメモリでの処理が行われるようになったため、tempファイルを作るなどのアプローチに頼っていたGruntに対する速度的な優位も生まれた(そして明らかに初期は「Gr**tに比べて速い」を売り物にしていた記憶がある)。gulp流のStream哲学に合わないサードパーティのtask pluginをブラックリスト認定するなどの行動にも出るなど開発陣は極めて急進的な側面もあるが、思想という点では中々にmodernな路線に進化する事になった。

けれども、gulpには問題点があった(と認識している)。せっかくプログラマブルかつ関数をタスクの単位としたのに、肝心のタスクとなるmap関数を作ろうとすると、gulpのstreamに併せて変換メソッドを隠す為にdirtyな関数を作り、クロージャとしてタスクを返すスタイルが、処理モデル上、強制されるので、その局所的に発生した汚物を隔離する為にtask pluginの形式でパッケージ化する必要が出てしまった。これはGrunt文化を引きずっている面もあるだろう。結果、「関数を組み合わせる事が出来る = 本来的には各種ツールチェインがビルドシステム構築用に提供しているAPIをそのまま使う」という事ができずに、隠蔽の向こう側に一段置いてから使う羽目になってしまった。本家のトランスパイラではなく、task pluginのアップデートに私のビルドチェーンが左右される日々の始まりである。peer dependency指定なら良かったね? いやいや、peer dependencyだってsemver的にmajor upがかかったら対応待ちだぜ?

理想的なgulpに必要だったのは、そこらへんの普通のトランスパイラのAPIをgulp streamに対応したtransform関数に変えるビルトインファクトリだったのかもしれない。これがあったならgulpはhigh modernなビルドシステムなり得たに違いない!(と私は勝手に思っている)。もしくは、Rxのような、もっと別物の抽象機構に乗っかっておくべきだったか。いずれにせよ、これらは夢物語である。

もうすこし汎用ビルドシステムの四方山話は続く。JavaScriptの変換に限ると、asterと呼ばれるツールが登場した。「お前らfile streamとか言うけど、jsファイルをやり取りしてるじゃねえか。毎回パースするの無駄だからASTを直接流そうぜ」というアプローチ。コンパイラとしては非常に正しいツッコミ。だが、これはイマイチ市民権を得られていない。考えられる理由は、シンプルに言ってgulpを上回る魅力が無いからであろう。コンパイラ的に正しいと言っても、それはアーキテクチャが綺麗というだけで、これで変換したコードからバグが消える訳でもない。毎回パースを行い抽象構文木に変換する処理は確かに計算機資源の無駄だが、そんなものはハードウェアの進歩で解決している。GruntではディスクI/Oがボトルネックだったが、gulpではstreamスタイルを強制した事で解決「させた」。というかそこが重要ではないのは, Grunt、gulpともにデフォルトでインクリメンタルビルドのためのツールチェインを備えていないことから明らかであろう。変更監視と都度フルビルドの体力勝負で賢いビルドが悩みがちな問題を解決してしまったのだ。それと、エコシステムの問題。いくら「フロントエンドWeb」が浮気性だからと言っても、ビルドツールという彼女を何回も乗り換えるほどの好色はそこまでいない。おまけに各種変換ツールチェインはES3~5のコードこそを中間表現として、そこに対して独自のASTを生成し独自の変換技法を尽くしているのが当たり前になった今更になって、各自の手の内を標準化しようなどと言うのはいささか困難な話であろう。

と、いうのがgulp一派に対する弁護と批判と多くの私情を交えた説明。

コマンドラインが一級市民の国

で、TypeScriptとかbrowserifyとかbabelとかfacebookのreact-toolsの話に戻ると、彼らはあくまでもコマンドラインからの使用を一般的な利用におけるfirst class citizenに取り扱う。一応、謎ビルドシステム・謎IDEからも遊べるようにnpm packageとしてのプログラマブルな利用も可能だぜ、というスタンスに近い。gulp pluginを公式にメンテしたりもするが、あくまでもtier 2であり、公式でなく野良協賛者が出してるgulp pluginともなると、公式も薦めこそするが「こんなのもあるから使ってね!」みたいな扱いに近いように思う。

なので、件の記事で話題になっているTypeScriptやbrowserifyについては、そもそもgulpとの相性が悪い道具なので、「makeの方が良いじゃん!」と言われたら、「そうですね」と頷くより他は無かったりする。

makeじゃダメなの

結局、makeじゃダメなん?という疑問については、私はダメじゃないと思うけど、なんだかんだで.PHONYとか書かなきゃいけないのだし、謎オプションを調べるのとgulpのタスクをコピペするのは特に大差がないし、むしろプラガブルな設計してるものをコピペする方が楽だし、だいたいプロジェクト始めるときに1回調べるだけなのは一緒じゃね?(後は使い回しになるよね?)と思う。

gulpの利点としてプログラマブルであることが挙げられる点についても、「そもそもビルドツールに条件分岐などのプログラマブル性を求めるべきではない」という声はあるものの、そうは言っても人類は、今までAutotoolsとか、その他メタビルドシステムを(makeをバイトコードと看做す形式で)何度か発明してきてしまったわけで、いずれはどこかでプログラマブルな需要を吸収しなければならないので今更な問題のように思う。まあ、程度問題として全部meta-metaにやられても困るので、どの程度までプログラマブルを許容するdesignにするかという問題はあるのは同意だけど、それはもっとミクロな個別の問題だと思いたい。

そして、プログラマブル性を求め始めると、なんだかんだで完全ないし高水準なスクリプティング機構が欲しくなるので、ホスト言語で書けるように作りたくなるのは仕方ないんじゃないかなとは思う。特にコンパイラのオプションとかは、3つで済む筈が無くて、それをコマンドライン1行で書くのは地獄だし、幾つもオプションを作るのも冗長なので、スクリプタブルなフラグで管理したいという気持ちは尤もであろう。

それにJavaScriptの場合、Cとかのような原始的なincludeベースのビルドシステムじゃなくて、コンパイラがエントリーポイントさえ分かれば後は依存関係を全部掌握してしまうモデル(というかコンパイラが明示的に情報を出さないと取れない)ので、GNU Makeの機能をフルに使ったインクリメンタルコンパイルなんてものは期待できない。結局、.PHONY buildとか書かないと、エントリーポイント以外のファイルを弄った場合にビルドできなかったりしたり(もちろん私のmake力が足りない可能性は十分にありますよ、ええ)。

ともかく、なので「Makefileで完結するじゃん!」なんてことはそんなに無くて、共通インターフェースのコマンドディスパッチャとしてMakefileを書きつつ、実態はgulpなり好きな物に投げろと言う感じになる。ここらへんはMozillamachとかを連想してもらえればわかりやすいと思います。

この手の議論をするときに捨て置かれがちな話に、クロスプラットフォーム対応の話がある。ついつい世の中のソフトウェアエンジニアは * nixでしか仕事しない前提で話を進めてしまったりするのだけど、特にWebエンジニアはOSXLinuxしか使ってない前提で話してしまうのだけど、世の中そんなことはなくて(当たり前だよね)、Webフロントエンド(Web制作)の場合、エクセル入稿やワード入稿とかがあったりするので、Winで開発してる人がそれなりに居たりする。そういうのが無くても、Windowsで開発していたりする。別にIE対応がどうとかじゃなくて。そういう人にとっては、cmd.exe叩いても動かない可能性が非常に高いmakeよりも、node.jsさえ動けばとりあえず動くGruntとかgulpの方が遥かに簡単だったりする。黒い画面は恐ろしいのだ。少なくともホスト言語の環境が動けば、とりあえず動くというのは非常に強みで、Gruntがなぜあそこまで流行ったのかは、クロスプラットフォーム耐性の高さとコピペビリティの高さだと言っていいと思う。もし、あれが*nixでしか動かなかったら、ああは流行らなかっただろう。みんなbuild.shかMakefileかbuild.batを書いている。おそらく今でも。

そういう諸々の状況を考えると、「makeでいいじゃん」というのは頭では納得がいくし、確かに壮絶に車輪の再発明を回させられている趣はあるのだが、なんだかんだで再発明された車輪を回した方が便利なケースも結構あるよねと感じるわけです。思想的には最も急進派であるgulpも、gulp pluginを使わずに自分でモジュール呼んだりchild_process.spawnしたものを呼び出すだけみたいなハイブリッド運用で回すといい具合に適材適所になったりする。

コピペでビルドツールを作るのか?というのは、確かに知力を武器として飯を食うエンジニア的には敗北感はある。が、落とし穴を掘りにくいdesignであるならばコピペビリティは正義である。我々は最強のビルドツール設定を書きたい訳ではなく、とにかくビルドチェーンを作りたいだけなので、コピペビリティ上等みたいなpragmaticぶりで良いのではないかと思う。いやあまあ、同僚がクソみたいなビルド設定作ってきたら、悪態をつきながらコードレビューで闘えば良いのではないかな。闘うのが面倒くさくなったら、コッソリ好きなスタイルに上手い事リファクタリングだ。

というわけで、なぜ人は再発明を繰り返すのか?という問いについては、一種の諦めと、ツールチェインの特性の問題と、「何十年も前に作られたCとUNIXの降霊方法を今更覚えたい若者はそんなに居なかった」という結論で良いんじゃないかと思います。C/C++Javaに次いで10年ぶりにビルドツールチェインを作る機会に恵まれたのだから、知恵は活かしつつ新しいものを使いたいのは人間の好奇心。コンパイルと言っても、C/C++Javaに比べればさしたる作業をしているわけではないのだし、コンパクトで調教しやすい物で良い。これで解決できないものが出来たら?今のJavaScriptの領分ではないので諦めてC++を書こう。GYPが君を待っている。個人的にはRust(のcargo)にも頑張ってほしい。

あ、余談だけど、この分野、たぶんgolangで依存関係を書けるコマンドディスパッチャを上手い事作れると、一発当てられそうな機運はあるなーと思った。既にあったかもしれない。ハードボイルドな燻し銀のgolangを使えば、おじさんも若者も大満足でwin-winなんじゃないかと。golangの特性的に妥当な感じもあるし。それがJavaScriptの文化に受け入れられるかは知らないけれど。