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

ソフトウェアの設計に思いを馳せた何か

software

http://www.slideshare.net/saneyuki/my-thoughy-about-beyond-flux

(主にGUIの)アプリケーション設計的なものをぼんやりと考えていた時に、色々思いついたことを書いた。特にどこかで発表するという予定もないんだけど、作ったからには手元でタンスの肥やしにするのも勿体無いので貼っておく。「まあ、そうだよね」とかそういう感じでしかないし、何か真新しい内容があるわけではない。

結構散漫な話なので上には書かなかったんだけれども、ソフトウェア開発において人はよくベストプラクティスを求める傾向にあるけれども、現実には「悪くはない」程度の解を選んだり、バッドプラクティスやアンチパターンを避ける方が総じて上手くいくように感じている。理想形を探り求め続ける限りに天井は無いが、「良くも無いが、最悪に面倒臭いことは回避している」であれば下限が決まっているので、程々に上手くやれるという認識でいる。

それと、

ソフトウェアを設計していていくつかの設計案のうちどれがいいのか決めかねるようなときは、「とりあえずOSがやっていることをまねる」というのが良いノウハウな気がしている。だいたい我々が直面するような問題というのは、OSがすでに直面していて何らか解決されている。

という話は真であると思う。

最近のJavaScriptはスクリプト言語のジャンルで必要な機能がだいたい揃った気がする(言語的な範囲では)

javascript

少し前にTypeScriptと素のECMAScriptとfb::JSXを混ぜてcompile -> linkする記事を勤務先のブログに書いたのだけれども、それ以後に色々思った小話を一席。

先述のエントリで示した構成を趣味プロジェクト含めて2~3度試した結果、現代のJavaScriptは書き捨て的なスクリプトから、(漸進的な)静的型付けまで、言語機能的なところではソフトウェアの規模に応じて、自由にスケールアップできるという実感を得るに至った、という確信を得た。インクリメンタルに移行できるので新旧コードの混在もできるし、素のJSじゃないとできないものは、それにやらせればいい。これはTypeScriptなりbabelといったcompilerやAST操作ツール群の成熟と、npmエコシステム、ES6というベースラインの更新が絡み合っている。

こうしてコードベースの規模に応じて自由にあれこれできるのは非常に楽だなと思う所存。それに今のJavaScript処理系(Chakra/SpiderMonkey/V8/JavaScriptCore)はどれも相当に速いので、速度が直接的にボトルネックになるよりも、コードや設計のマネージの方が主題になることの方が多いと思う(これは計算量がワーストケースに落ちない戦略・設計的なマクロなアプローチを含む)。micro optimizationが必要なケースもあるだろうけど、だいたいは

  1. ボトルネックを計測
  2. 頑張る
  3. 頑張って無理なら諦めてもっと速い言語を使う

な感じになるだろうし。

TypeScriptの提供する以上の言語機能が欲しい場合はどうするか?という問題は、素直に言語を乗り換えるのが良いのでしょう。あれより高水準な表現力とか静的型付けを持つ言語はだいたいJVMで動く言語かネイティブコードを吐いて動かす言語になるし、それが必要になる場合は速度面でもJavaScriptの適応可能範囲を超え始めるだろうから、乗り換えの時期なのだろう。

例外として、TypeScript以上の型表現を持ったAltJSというとScala.jsとかがあるけれども、各処理系のfirst tierがECMA262である以上、なるべくそれに近い方が色々と予測性が高いのとTypeScriptも1.6な今ならあれで多くのケースは解決すると思うので、私はあまり興味がない。大抵のケースは、Scalaが欲しいなら素直にon JVMな実装使った方がいいと思う。JSVMの上で語るにしても、WebAssmebly(のGC intergration)が来てからじゃね?

話が逸れたけど、Closure Compilerとかに頼らずとも、大きなソフトウェアを作るまでの言語機能のスケールができるようになったのは、いい時代になりましたね、という趣。

RxJSのいろいろ雑感(2015年8月末編)

javascript
  • 8月末に書いたメモ書き
  • どこに書いたか忘れそうなんで貼っておく
続きを読む

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

javascript ReactiveX

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の整理

software

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

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

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

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

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

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

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

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

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

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

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

architecture

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#の連中が取り組んだ事のアレンジですよ全く!