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の文化に受け入れられるかは知らないけれど。

リュックサックを新調した話

リュックサックが欲しくなった

大学生3年の頃に、BERMASの42cmのブリーフケースを買って以来重宝している。単体で1.5kg弱あるんだけど、その変わり生地が非常に硬く、型もしっかりしている上、おまけにショルダーベルトまであるので非常に使い勝手がいい。ポケットなどについてはリンク先を見てください。

大きな収納スペースが2つあるので、コミケのときは買った同人誌を一方のスペースに片っぱしから放り込むのに使い、もう一方に500mlペットボトルを2〜3本格納したりできるし、旅行のときは1泊程度であれば着替えやPCを入れたまま行動しつつサイズによってはお土産を格納できるくらいの容量があり、それでいて型崩れなども特にないので非常に便利に使っている(正確にはマチを広げるためのファスナーを一度修理したことがあるんだけど、実用上は必ずしも必要ない場所のファスナーなので、かなり頑丈です)

[バーマス ファンクション] BERMAS FUNCTION ブリーフ42 2層EX 60135-10 ブラック

[バーマス ファンクション] BERMAS FUNCTION ブリーフ42 2層EX 60135-10 ブラック

が、いかんせん日常の通勤に使うには単体1.5kgは重い上、ポケットの数や頑丈さもオーバースペック気味。なので、軽く散歩するときや通勤時は、高校生の頃に買った小さなショルダーバックを使うようにしていた。とはいえ、このショルダーバックは、当時地元のイトーヨーカドーで4千円くらいで買ったもの + 結構酷使してきているので、いい加減に生地の痛みが激しくなってきてしまった。PCなどを入れると形が崩れる上、内容量もMacBook Air 13などを入れるとそれだけでいっぱいになってしまう程度のサイズ感。おまけにショルダーバックということで歩いたときの斜め掛けが微妙な感じがあったので、新たな鞄をさがすことにした。

紆余曲折

探すに当たって設定した条件は以下の通り。

  1. リュックサック型であること
  2. 容量は大きくなくていい(大容量の用途には最初に述べた42cmのブリーフケースで十分だし、それで入らない荷物は私の日々の生活では扱わないし、必要になったら巨大なリュックサックを買えば良い)
  3. それなりに頑丈であること

リュックサックを選んだのは、運用上、両手を開けたいため、かつ片方の肩にばかり負担をかけたくなかったから。 容量は、日常使いのものなのでそんなに大きくなくていい。 頑丈さは、まあ、そりゃ求める。まして今使っているものが型崩れを起こした果てのものだから。

秋葉原ヨドバシカメラでのんびり探していたのだが、結構難航した。頑丈なリュックサックを求めると概して大きなものになってしまうか、見た目がビジネス向けすぎてダサいもののどちらかになってしまう。かといって小さいものを求めると、作りが雑だったり、見た目がいいものは本当に携帯と充電器とハンカチと財布とかしか入らないようなサイズになる。もしくはビジネスマン向けのダサい感じ。悩ましい。

邂逅

結局、今日も収穫は見つからないのか、やはり上野あたりでいろいろ店を回った方が良いのかと失意にくれかけた瞬間に、Timbuk2のQ BACKPACKを発見した。掘り出し物だ。棚の一番下の段の奥の方に眠っていた。容量はそこそこでお大きすぎないが小さすぎでもない、ポケットの数もまあそれなり。それでいて作りがしっかりしている。マチはそこまで太くないので嵩張らないし、背負ってもバックパックが後方に盛大に突き出すような不恰好な感じにはなりにくい。デザインも悪くない。いい。値段も8千円前後なので、失敗してもそれほどダメージは負わない価格。売り場の同価格帯の製品に比べると、1万円を超えていてもいい感じの作りをしているので、お得感はある(実際、メーカー希望小売価格だと1万円をはみ出ている)

というわけで、これのディアボロ(赤系)を買ってきましたとさ。他の色が無かったから詳細な比較検討はできなかったんだけど、割とカジュアル用途の鞄まで黒単色にはしたくなかったし、深いワインレッドでそこまで派手ではなかったこと、他の色がそこまで好みのトーンでもなかったので、結局置いてあった色でいいだろと判断。とはいえ、Martini Olive Surf Stripe(明るい緑)あたりは実は気になっていた。でも結構色が明るいので、そのうち飽きそう。 カーボンフルサイクルツイル(カーキ系だけど灰色に近い?)は、もっと緑・茶系だったらいいと思った(でも違う感じ)。てなわけで消去法でディアボロ(赤系)を選びましたとさ。

使い勝手どうよ

買ってみてだいたい3週間くらい使ってるんですが、マチが細い割に結構いろんなものを投げ込めるので重宝してます

option-tにPromiseへのキャストメソッドを用意したりなど

自作RustスタイルECMAScript用Option型ライブラリoption-tをいろいろアップデートしたので、リリースノートがてら書く。ご存知ない方はこちらをどうぞ。要はOption<T>/Maybe型だ。

Promiseへのキャストメソッドを用意した

ECMA 262の文化には、すでにMaybeもどきの標準仕様が存在する。ご存知Promiseだ。

だが、Promiseは非同期前提の操作になるので、同期的に実行するAPI群との親和性が良いとは言えず、かといって全てをPromiseにするほどでもない場合のために、わざわざこんなライブラリを作ったというのは前に書いた通り。

でも、今の世の中、どこかしらの関数がPromiseを返すじゃん?そこに上手く混ぜられるととっても楽じゃん? てなわけでcast用メソッドとしてOptionT.asPromise()を追加した。これにより、スムーズにPromiseと混ぜることができ、非常に最高な気分になれる。

こんな感じ。XHRを投げつつ、別のフラグの有無を確認し、両方が成功した場合に〜みたいなケースに有用になりました。

var option = new OptionT.Some();
var xhr = new Promise();

Promise.all([
  option.asPromise(),
  xhr,
]).then((tuple)=>{
  var responce = tuple[1];
  ...
});

ちなみに、Promise<T> -> Option<T>の変換は、非同期から同期への変換なんで無理ですやってない。

いろいろメソッドを追加した

追加しました。前よりも楽に書けるようになったと思います、どんなの使えるかはgithubissue一覧CHANGELOG.mdをみてください。

ここで注意点がひとつ. Option<T>.mapOr()Option<T>.mapOrElse()については、unstable APIという区分にしてある。理由は、取りうる仮引数の順序が、「デフォルト値生成」 -> 「map関数」の順序になっているため、直感に反するのではないかというのが理由。元のRustのstd::optionAPIが微妙っちゃ微妙なので、Rustの動向を無視してもいいのだが、ちょうど今のタイミングで、Rust側でも似たようなissueが立っていて、できることならもう少し様子見したいというところ。なのでunstableという区分にはしている。

まあ、変更するとしても仮引数の順序を入れ替えるだけで、挙動としては変わらないのと、そういう破壊的変更するときはsemver的にメジャーバージョン上げるので、使うのをためらうほどではないかもしれない。ただし、今の所はNODE_ENV = 'production'時以外は``console.warn()でうるさく警告出してるので気をつけて下さい。

一応フィードバックありましたらお知らせください。

RxJSのObservableのHotとColdの実装を眺めた

更新情報

  • UTC+9:00, 2015/03/29, 3:30くらいに、サンプルコードとかロジックの説明とかを修正した
  • UTC+9:00, 2015/03/29, 14:30くらいに、gistにした(エラッタの修正履歴を残すため)