/<role>/<domain>/DomainNameRoleみたいなディレクトリ階層よりも/<domain>/<role>/DomainNameRoleみたいな階層の方が素直だと思う

こんなの探せば10年とか20年前にも似た議論があって結論出てそうだし、ドメイン駆動設計系の人が実践論として言ってそうなものだけど。

アプリケーションフレームワークとかボイラープレートみたいなものをいくつか眺めていると、/Controller/SignIn/SignControllerみたいなディレクトリの組み方を薦めてるものが結構あるけれど、これは素直じゃないなと思った。

だいたいこんな階層になるやつ。

  • /
    • Action/
      • Signin/
      • Report/
      • Editor/
    • View/
      • Signin/
      • Report/
      • Editor/
    • Repository/
      • Signin/
      • Report/
      • Editor/
    • UseCase/
      • Signin/
      • Report/
      • Editor/

何が素直ではないと思ったかというと、

  1. 具体的な問題領域よりも先に教条主義的なコードの役割分担が優先されている
    • 問題領域に応じてHelper増えたりとか色々あるのが本当だよね
    • 例えばバージョン情報みたいな画面だと View だけに色々ベタに書いていいはずで(バージョン情報の他問題領域から再利用とか、普通そんなに起きねえよ)
  2. 結果, 同一の問題領域のコードなのに木構造的に再従兄弟みたいな位置にいるコードが多くて見通しが悪い
    • 凝集度悪くね?
  3. 問題領域に応じたコードの組み方がやりにくい
    • 例えば「ReportドメインのViewはReactじゃなくてVue使いたい!(そちらの方が技術的に向いている)」みたいな需要に合わない。この構成だと、例えばView/の下の一箇所を変えると、隣接するディレクトリに配置されたコード全てを同じように変えたくなってしまう。それが人情ってもんです。

な辺り。

あとで移行とかリファクタをしようと思った場合に一番面倒なのは 3 だし、普段コードをいじるときは 2 がストレスになるけど、何れにしても「問題領域に応じて最適な道具を用いる」という思想とは噛み合わせが悪いし、私はそういう思想なので、息苦しさとかデメリットばかりなように思う。なので、自分がゼロから組む場合はもっぱら:

  • /
    • Signin/
      • Action/
      • View/
      • Repository/
      • UseCase/
    • Report/
      • Action/
      • View/
      • Repository/
      • UseCase/
    • Editor/
      • Action/
      • View/
      • Repository/
      • UseCase/
      • Helper/

みたいに, 問題領域の方を階層の上に置くようになった。 こっちの方が問題に合わせてコードを書く、という場合は素直だと思う。

/<コードの役割>/<問題領域>/問題領域_コードの役割.拡張子 みたいなディレクトリ構成を採用してるフレームワークは、そのフレームワークが想定する・楽にできる問題領域が1つ〜2つ程度なんじゃないか、それ以上を単一のアプリケーションに入れ込むことを想定してないんじゃないかと思うし、用途としてはそんなに向かないだろう。

だからと言って「マイクロサービス最高!時代はマイクロサービス!マイクロサービスしない奴は何をやってもダメ!」とか言うつもりはないんだけど、そこらへんの射程の距離感は常に意識しておくべきで、それを忘れるとコードが無秩序に増え続けるのをウッカリ見過ごしてしまいそう。

rustcを通らないコードは間違っている

Rustを書いていると、慣れるまではrustcに頻繁に怒られる。慣れても結構怒られる。とにかくrustcに怒られる。きっと貴方はこう思うだろう、「Rustはなんて煩い言語なんだ!俺の愛する****(好きな言語の名前を入れてください)ならばこんなことはないのに!」

このように「rustcが煩くて俺のコードが通らない」場合、とりあえず自分のコードが間違っていると疑って問題はない。え、「俺のコードは正しい」? 違う違う、コードのロジックの話じゃない。「そのコードがRust wayではない」という点で「間違っている」。microなスタイルからmacroな設計まで、ありとあらゆる点でRust的なコードであることを暗黙的ではあるが極めて強く要求する。それがRustというプログラミング言語だ。時たまrustcが間違っていることもあるんだけど、体感値として97%はrustcの方が正しいと言っていい。

なぜこのように「不自由な」言語なのか? それはRustは「人間が考えなくても静的に解析できる情報を増やせば、コンパイルの時点でリソースの取り扱いに関するバグを減らせる」という世界観で構築されているからだ。それを実現する為に「言語仕様としてセマンティクスを強く定義し、rustcでそれを機械的に徹底検証する」というモデルを提供している。

こういう世界観で構築されているので、論理的に検証できない・すなわちコード内に自明ではない整合性はRustにおいては、しばしばrustcに禁止させている。パワーバランス的には人間よりもrustcの方が強い。コンパイルの通らないコードはそもそも動かないからだ。セマンティクスと相俟って人間はrustcが解釈できるようなコードを書く必要がある。GoとかTypeScriptよりは間違いなく圧が強い。見返りに、人間はRustの世界観を受け取る。Rustはコンパイルさえ通れば、(後述する例外はあるが)書かれたコードの通りに動く。書いたコードがバグっていればバグ通りに動く。 Noneunwrap()すればそこでクラッシュする。 { ... }でスコープを縮めてやればlockは縮めた分だけすぐに解放される。それがRustである。「〜っぽいコードを書こう」なんて、ゆるふわは無い。勧められるままに書かねば何も動かないか無駄に歪なコードが残るだけだ。

このように世界観の主張が強いので他言語のコードを安直に置き換えても上手く行くとは限らない。moveで済む場合はborrowせずmoveせよ、goroutineみたいにスレッドをたくさん建てずアクター的にせよ(これはownership制約と同時にカーネルスレッド使ってるのが理由だったりもするけど)、下手に高次の言語からportするよりも、スレッドも何も使ってないCやJavaScriptからportの方が簡単かもね。とにかくRustの世界に合わせてコードと設計を変えなければならない。さすればrustcはコンパイルを通してくれる。

この思想故、Rustは言語機能こそ多いが局所局所で取れるmicroなパターンは極端に多くない。syntax的には組み合わせられても、micro/macroでのownershipやlifetimeによって組めないパターンが幾つも現れる。

もちろんそんな理想論ばかりが通ることはない。C/C++の資産との接続、明らかに安全かつ性能のためのdirty hack。そんな時はコードをunsafeに括り出せばよい。ただし全てunsafeにしてはいけない。unsafe由来のコードはRustの世界を容易に侵食し、コンパイルが通ったコードがいきなりセグフォで落ちるようになりかねない。なので、必要最低限の分だけ括り出さねばrustcの加護は得られない。このため、FFIを用いるbinding crateは***-sysという薄いFFIするだけのwrapperと、それの上にRustの殻をかぶせたAPIの二段構成を取ることになる。 いくらrustcが全てを視てくれると言っても、そのために全ての変数の型やlifetimeを明記するのは怠い。故に推論による省略が出来る。

rustcに無制限の自由を差し出す代わりに、偉大なるrustcの監視の目を用いてコードの穴は塞がれる。代償とした自由は大きくも見えるが、現実的に取り得るコードのパターンに於いては概ねプログラマ側に自由があるので問題はない。発想が少々oxydizeされるだけだ。プログラマはロジックを書くのに専念すればよい。IDEに一定範囲の自由を売る代わりに、IDEによってコードやリポジトリに対する人間の認知力を拡張するのと発想は同じ。ScalaとかKotlinとかJavaとかC#とか、あの辺りの作法と大差はない。問題のレイヤーを低次に下げた結果、機械に売り渡す自由は増えたが、多くはレイヤー固有の事情なので問題にはならない。

(とはいえ今のRustはIDE支援が半端で色々片手落ちになってるので、Rust Projectの2017年のRoadmapに「IDEを頑張る」って入ってるんだけど)

言語に合わせて思考と設計を変える。今までの固定観念を捨ててRustに合わせて再構築し直す。これがRustでコードを書くということだ。

rust-bindgenのupstreamが変わった

cargo:bindgenのupstreamがYamakaky/rust-bindgenからservo/rust-bindgenに変わった。変わった結果どうなったかというと、C++のヘッダのパース機能が大幅に改善したりする。

rust-bindgenを知らなかった人に簡単に解説しておくと、clangを使ってC/C++のヘッダをパースして、それを元にRustのC FFI binding用途のglue codeを生成するcrateのこと。

元々rust-bindgenはRust ProjectではなくJyun-Yan Youが始めたらしい。らしいというのは「git logを辿った限りだとそうなっている」以上のことを言えないから。その後、メンテナー間での移譲があったのかどうかはよくわからないが、2015年末の時点ではYamakaky/rust-bindgenがupstreamとなっていた記憶がある。自信ないけど。

Mozillastylo(Quantum CSS)をやる関係でC++で書かれたGeckoとのglue codeを作る必要が出て、2015年の後半ぐらいからservo/rust-bindgenとしてforkしてMozillaのpaid staffが用が足りるようになるまでコードを書いていた(今も続いている)。元々のbindgenはC++サポートはそんなに強く無かったと記憶しているけど、Mozilla側で手を突っ込みまくった結果として改善され、たとえばleveldbのC++ヘッダが丸々パース可能だったりする(実体験)。

で、merge into upstream! · Issue #21 · servo/rust-bindgenというissueが立っていて、色々あったんだけど、最終的にservo側がupstreamを引き継ぐことになった。

RFC 1774で2017年のターゲットの一つにC/C++との相互運用を挙げているし、Servoがupstreamを引き継ぐのは間違っていないのではないかな。RFCを考えるとrust-lang-nurseryが引き継ぐのが一番いいけれども、Rust Project最大のパトロンであるMozillaの一門であるServo Projectの下に置いておくのは妥当なように思う。

生成されるコードは何も指定しないと大きい。依存先を含めてシンボルを再帰的に変換していくので当然といえば当然である。 実用上はpublic apiだけを変換対象のホワイトリストに指定することのほうが多いと思う。なんとか-sysを作るための道具なので、そこで生成したコードをそのままアプリケーションコードから呼ぶべきではない。

使い方?それはREADMEでも読めばいいんじゃないかな。

2016年やったこと

本業

  • 色々あった
  • 2016年も大変な一年だった

前半

後半

github:voyagegroup/eslint-config-fluct

  • internalに使ってるルールを、そのままpublicにした感じ
  • npm公開とかは検討中. 別にnpm publishしなくても再利用できるしね.

Open Source Development

facebook/react

ReactiveX/rxjs

rxjsのpull requestはrebaseが必要だとnetflixのメンテナーが適当にcherry-pickしてmasterにlandするので、ちゃんと取り込まれたものでも、closedとmergeが両方あったりする

Microsoft/TypeScript

saneyuki/option-t.js

  • 細々と必要に応じて更新してたり

karen-irc/karen

  • 趣味プロジェクト
  • 完全に自分用のビルド設定遊び場と化している……

servo/servo

  • 今年も殆どinactive(何やったか忘れたほどに……)
    • mailing listとかissueとかpull requestはだいたい見てるんで、何やってるかは知ってるんですが……
  • ちょっと手が空いた時に簡単なpatchを気晴らしに送る、みたいな関わり方だった.
  • もう少しやりたかったけど、そこまでの精神的余裕がなかった

総評

  • 色々あった
  • 何かを新しく学んだ、という印象は少ない一年だった
    • なんかアウトプットだけで終わった感はあるんだけど、その割にインパクトのあるアウトプット思ったよりも少ないですね……
  • 2017年は、インプットとアウトプットをもう少し増やしたい

備忘録: yarnpkgで依存パッケージのガーデニング作業を楽に行う

前提

How to

  1. yarn outdatedとかで更新が必要そうなものを眺める
    • いきなり2へすっ飛んでもいい
    • バージョンをstableとかdevとかnextで指定している場合は出てきたり出てこなかったりするけど
  2. yarn upgrade-interactiveする
    • バージョンをstableとかdevとかnextで指定している場合は出てきたり出てこなかったりするけど
  3. 2を繰り返す
  4. yarn upgradeで, 表に出てこない依存関係含めてyarn.lockを更新する
    • 今のところ, ./node_modules/を消して, yarn.lockを消して, yarn installしてyarn.lockを再生成した場合と同じ効果が得られる

Node学園#23で広告ジャバスクリプトの話をしてきました

スライドに書いてない詳細とか補足とかは, 勤務先のブログに書くかもしれないけど, もう少し私的な記録として.

Node学園#23で, 今年の始め〜夏ぐらいにやっていた話をしてきた 元々, 東京Node学園祭2016にCFP出してたんだけど, 甲斐なく落選してしまったので, お焚きあげということで発表させてもらった.

コアの実装だけで言えば2週間ちょいくらいで片付けられたし, これ以外にも細々とした色々をやっていたので, 実働時間としては大して多くは無いんだけど, 精神的には今年で一番手間暇をかけたような感じだった. 実装が難しいというよりも, とにかく面倒くさい. 原理的に何が出来て何が出来ないのかを各種specを元にちゃんと詰めて実装する面倒臭さ.

そんな感じでなので, どこかの勉強会で発表して締めということにしてみた. 締めとして勉強会とかブログを一本まとめを出せるように仕事するのは結構楽しいというかチャレンジしやすくていいね. なるべくこういうスタイルで仕事をこなしていきたいものである.

オンライン広告については, テクノロジ的に面白いことが多いんだけど, Webのユーザーとして見た場合にクソみたいな広告案件と接することも多くて, 総体としてあんまり印象が良くないのが寂しいね. 仕方ないが.(編注: 個人の感想であり, 勤務先とは関係のない意見です).

そろそろJavaScriptは飽きてきたので, RustとかScalaとかJavaとかC#とかGoとかC++とか何かで新しい何かを書いていきたいものだが, はたまた.

東京Node学園#20でtry-catch使うなという話をしてきました

東京Node学園#20「Don't use try-catchというタイトルで話してきました。

option-tの話

最初は手前味噌なoption-tの話を主題にするつもりではあったんですが、そんなライブラリの話とかしたって特にトリッキーな実装をしているわけでなし、全然面白くないし、別に特定のライブラリを使わなくても実現できる内容なので、もう少し広めの話題ということでtry-catchを安易に使うのをやめようという話にしました。

宣伝がてらoption-tの話も入れていますが、option-tのREADME.mdにも書いている通り、これのみが正解というわけでもないし、TypeScriptなどの静的型付けな支援が無いと使いにくいこともままあるし、依存関係を増やす元になるので、無理して使う必要も無いでしょう。インスタンスメソッドを組み合わせて使うとか、そういったことに魅力感じる場合に、選択肢の一つとして選んでいただければ幸いです。

質疑応答

当日は質疑応答の時間が取れなかったので、懇親会で聞かれた内容について。

Promiseの型定義次第で解決されるのでは無いのか?

これについては、Twitterでも回答させていただいておりますが、私個人の認識としてはノーだと思います。

もちろん一定の緩和にはなりますが、Promiseコンストラクタに渡すexecutor内でのthrowに対してもPromise.prototype.catch(e)は動作してしまうので、type paramterを増やしても制約として機能しませんし、むしろ特殊化したE以外の値が実行時に飛んできた場合に各種制約の前提が崩れるので、全体としてはむしろ状況が悪化してしまいます。なので、ES6 Promiseのdesign上, catch()any型がわたってくるというTypeScriptの定義は正しく、これに基づいて対応策を考えるしかないのではないかと。

もちろん、ES6 Promiseとは異なるdesignになっているPromiseを使う場合は、この限りでは無いですが、そこまでしてcatch()でハンドリングしたいかというと、微妙かなーと。

なんかLintとかでbanできないの?

↑で。

ESLintの/*eslint-disable*/~/*eslint-enable*/の良いところは、限定解除の明示化すなわちRustlangのunsafeブロックなので、使いたい場合をopt-in化できるのは、まあアリなのでは無いかと思います。

バグは積極的にクラッシュさせて洗い出すほうがいいというけど、そうは言っても何でもかんでもバグはクラッシュって厳しくない?

厳しいケースもあると思います。

ただ、そういう要求がなされる場合(たとえソフトウェアのバグがあっても回復を試みなければならないシステムとか)、

  • 本当にエクストリームなアプリケーションは、そのような問題の解決についての研究などを元にしたdesignで構築されるべき
  • そうでないアプリケーションに関しては、だいたい殺したほうがいいが、それでも「もしかしたら死ぬかもしれない処理は別立てのプロセスで処理する」などのように、影響範囲を分割するなどして対処するべき
    • この手の非同期イベント駆動にしたほうがいい箇所こそNode.jsの強みでは。

ではないかと思います。 (そんなに詳しく無い分野なので、具体的にどの研究が良いなどの突っ込んだ話はできません……)

あと、そもそも何かしらのアプリケーションはリリース前にQAとかデバッグやるんだから、その期間に可能な限りバグを発見できるようにしたほうが良いというスタンスです。

これを守るとオブジェクトの生成とかで遅いケースってあるよね

あると思います。 ですが、全体・通常としては表現の明瞭さを重視するスタイルを取るべきで、ハイパフォーマンスを要求されるモジュール・アプリケーション・プロジェクトに関しては、それを重視した規約にする、というのが現実的ではないかと思います。

どうせ遅かったら究極的にはCとかアセンブラ書くしかないんだよ!

Joe Duffy - The Error Modelは読んで面白い?

読みたいと思ってるなら読んで損は無いと思う。 別に無理して読むほどじゃ無いけど、新卒の子とかには前半部分だけでも読ませたい

まとめ

大体こんな感じ