2016年やったこと
本業
- 色々あった
- 2016年も大変な一年だった
前半
後半
- 今の時点で表に出せる内容はそんなにない
- 社内向け管理画面のGUIを再設計するみたいな仕事を細々とやってたけど、だいたい以下でやった内容のやり直しなので、今のところ面白みのある設計とかライブラリとかを何か公開する予定はないです。
github:voyagegroup/eslint-config-fluct
- internalに使ってるルールを、そのままpublicにした感じ
- eslintとeslint-plugin-reactとeslint-plugin-nodeのルールだいたい全部吟味してある.
- 毎週のように依存パッケージを上げ続ける努力 - VOYAGE GROUP techlogに書いたように、定例で更新してるので、まあ最新のルールもガンガンenableにします.
- 更新止まった時は, 誰かが燃え尽きたのだなと思ってください……
- npm公開とかは検討中. 別にnpm publishしなくても再利用できるしね.
Open Source Development
facebook/react
- [Fiber] Assign ReactTypeOfSideEffect to ReactFiber.Fiber.effectTag correctly
- React Fiber周りを読んでいたら見つけたので直した
ReactiveX/rxjs
- refactor(operator): make some _unsubscribe protected
- refactor(FromEventPatternObservable): remove to use tryCatch
- chore(DeferObservable): add benchmarks & minor perf
- refactor(throttle): remove tryCatch/errorObject
- chore(typings): fix compiler errors when using tsc v2.0
- refactor(Subscription): return always subscription when calling Subscription.add()
- refactor(Action): add 'T' type param to Action
- refactor(audit): rename from 'inspect' to 'audit'
- refactor(scheduler): specify an explicit type
- chore(from): add benchmarks for the path of IteratorObservable
- fix(Observable.prototype.forEach): removed thisArg to match es-observable spec
- chore(typings): increase combineLatest's signature compatibility with v4
- chore(typings): increase zip's signature compatibility with v4
- fix(typings): make HotObservavle._subscribe protected
- refactor(switchMap): clarify 'any' types which are used in SwitchMapSubscriber
- chore(Subscriber): add comment why Subscriber.destination is 'PartialObserver
' - refactor(MergeMapTo): clarify 'any' types in MergeMapToSubscriber
- refactor(MergeMap): clarify any types which are used in MergeMapSubscriber
- refactor(SubjectSubscription): remove 'any' type from SubjectSubscription
- https://github.com/ReactiveX/rxjs/pull/1740
rxjsのpull requestはrebaseが必要だとnetflixのメンテナーが適当にcherry-pickしてmasterにlandするので、ちゃんと取り込まれたものでも、closedとmergeが両方あったりする
Microsoft/TypeScript
- Fix #9550: exclude 'this' type parameters from unusedParameters checks
- 確かbetaのリリース直前に駆け込みで見つけてpatchを送った記憶がある.
karen-irc/option-t
- 細々と必要に応じて更新してたり
karen-irc/karen
- 趣味プロジェクト
- 完全に自分用のビルド設定遊び場と化している……
servo/servo
- 今年も殆どinactive(何やったか忘れたほどに……)
- mailing listとかissueとかpull requestはだいたい見てるんで、何やってるかは知ってるんですが……
- ちょっと手が空いた時に簡単なpatchを気晴らしに送る、みたいな関わり方だった.
- もう少しやりたかったけど、そこまでの精神的余裕がなかった
総評
- 色々あった
- 何かを新しく学んだ、という印象は少ない一年だった
- なんかアウトプットだけで終わった感はあるんだけど、その割にインパクトのあるアウトプット思ったよりも少ないですね……
- 2017年は、インプットとアウトプットをもう少し増やしたい
備忘録: yarnpkgで依存パッケージのガーデニング作業を楽に行う
前提
How to
yarn outdated
とかで更新が必要そうなものを眺める- いきなり2へすっ飛んでもいい
- バージョンを
stable
とかdev
とかnext
で指定している場合は出てきたり出てこなかったりするけど
yarn upgrade-interactive
する- バージョンを
stable
とかdev
とかnext
で指定している場合は出てきたり出てこなかったりするけど
- バージョンを
- 2を繰り返す
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の型定義次第で解決されるのでは無いのか?
TypeScriptのPromise型がPromise<T, E>になればある程度解決される? #tng20
— teppeis (@teppeis) 2016年4月5日
これについては、Twitterでも回答させていただいておりますが、私個人の認識としてはノーだと思います。
もちろん一定の緩和にはなりますが、Promise
コンストラクタに渡すexecutor内でのthrow
に対してもPromise.prototype.catch(e)
は動作してしまうので、type paramterを増やしても制約として機能しませんし、むしろ特殊化したE
以外の値が実行時に飛んできた場合に各種制約の前提が崩れるので、全体としてはむしろ状況が悪化してしまいます。なので、ES6 Promiseのdesign上, catch()
はany
型がわたってくるというTypeScriptの定義は正しく、これに基づいて対応策を考えるしかないのではないかと。
もちろん、ES6 Promiseとは異なるdesignになっているPromiseを使う場合は、この限りでは無いですが、そこまでしてcatch()
でハンドリングしたいかというと、微妙かなーと。
なんかLintとかでbanできないの?
no-restricted-syntax: ["error", "TryStatement"]#tng20
— Toru Nagashima (@mysticatea) 2016年4月5日
↑で。
ESLintの/*eslint-disable*/
~/*eslint-enable*/
の良いところは、限定解除の明示化すなわちRustlangのunsafe
ブロックなので、使いたい場合をopt-in化できるのは、まあアリなのでは無いかと思います。
バグは積極的にクラッシュさせて洗い出すほうがいいというけど、そうは言っても何でもかんでもバグはクラッシュって厳しくない?
厳しいケースもあると思います。
ただ、そういう要求がなされる場合(たとえソフトウェアのバグがあっても回復を試みなければならないシステムとか)、
- 本当にエクストリームなアプリケーションは、そのような問題の解決についての研究などを元にしたdesignで構築されるべき
- そうでないアプリケーションに関しては、だいたい殺したほうがいいが、それでも「もしかしたら死ぬかもしれない処理は別立てのプロセスで処理する」などのように、影響範囲を分割するなどして対処するべき
- この手の非同期イベント駆動にしたほうがいい箇所こそNode.jsの強みでは。
ではないかと思います。 (そんなに詳しく無い分野なので、具体的にどの研究が良いなどの突っ込んだ話はできません……)
あと、そもそも何かしらのアプリケーションはリリース前にQAとかデバッグやるんだから、その期間に可能な限りバグを発見できるようにしたほうが良いというスタンスです。
これを守るとオブジェクトの生成とかで遅いケースってあるよね
あると思います。 ですが、全体・通常としては表現の明瞭さを重視するスタイルを取るべきで、ハイパフォーマンスを要求されるモジュール・アプリケーション・プロジェクトに関しては、それを重視した規約にする、というのが現実的ではないかと思います。
どうせ遅かったら究極的にはCとかアセンブラ書くしかないんだよ!
Joe Duffy - The Error Modelは読んで面白い?
読みたいと思ってるなら読んで損は無いと思う。 別に無理して読むほどじゃ無いけど、新卒の子とかには前半部分だけでも読ませたい
まとめ
大体こんな感じ
option-tにResult<T, E>型を実装した(簡易報告版)
概要
- Error Handling - The Rust Programming Languageに書いてある
- API
- JavaScriptなのでownershipとかはないです
当該issue
使い方(v0.17時点)
ECMA262のIteration protocolsで遅延評価するIteratorを作る
これは何?
ECMA262 6th以降にはiteration protocols(と呼ぶべきもの)が導入されている、というのは皆さんもちろんご存知のとおりだと思います。これを使ってIterator<T>.next()
が呼ばれるまでmap()
などを実行しない(lazy evaluationする)Iteratorを作ってみようという話です。Rustのstd::iter::Iterator
とかC#のLINQとかが似たような挙動をしますよね。
今後、巨大なオブジェクトやリストに対してのIteratorが提供・出現した場合、毎回のように即時評価でfor-of
したりArray.from()
するなどは現実的ではないでしょう。以下のように書くことも出来ますが、
/** * @template T, U * @param {Iterable<T>} src * @param {function(T):U} mapfn * @return {Iterable<U>} */ function map(src, mapfn) { const iter = src[Symbol.iterator](); const next = function () { const { done, value } = iter.next(); if (done) { return { done, }; } const result = mapfn(value); return { done: false, value: result, }; }; return { [Symbol.iterator]() { return { next, } } }; } const inifinityList = [...]; map(inifinityList);
bind operator proposalがacceptされないことにはa( b ( c() ) )
のように書いていくケースの方が多くてダルい。俺はbind operatorの行く末を気にせずにArray.prototype.map()
のようにメソッドチェーンで書いていきたいんだ、その気持ち、わかります。
というわけで、ものは試しにiterator protocolに則る形で実装してみましょう.
Proof Of Concept
型がややこしいのでTypeScriptで書いてみました。だいたいこんな感じ。どうせES6に型アノテーションつけた程度に書いてるので、適当にアノテーション落とせば動きます。
ちなみに基本設計はRxJS v5を元にしています。IterableとObservableでDualityだ! だからというわけではないですが、評価結果のキャッシュはopt-inになっています(cache()
)。安易にキャッシュしてしまうと、mapやfilterのコールバック関数がDate.now()
などの副作用に依存している場合にバグってしまうので、明示的に使用する前提にしてます。
実用したい場合
大抵の場合は自分で再実装するか、Interactive Extensions (for JavaScript)あたりを待つか、新しい何かが出てくるのを待てば良いのではないでしょうか。
- github:ReactiveX/IxJS: リポジトリ公開されたばかりで今のところどうなるのかよくわからん
他にも、
- github:Reactive-Extensions/IxJS: こっちは古い版でIteration protocols未対応
- linq.js
- lazy.js
などがありますが、2016年3月3日現在の安定版ではiteration protocolには対応してない模様ですが、今のところはES6~のIteratorが必須になるようなケースはそこまで多くはないと思っているので、これらを使うなどで凌ぐというのも現実的だと思います。
RxJSで副作用を扱うにはどうするか - Schedulerを交えて
Rx.Scheduler
RxにはSchedulerと呼ばれる主要概念がある. 値がpushで飛んでくるというRxのインパクトの後ろに隠れがちなSchedulerではあるが, これにより, 処理系のスレッドモデル(並行性)と時間軸にまつわるタイミングの制御を統一的に扱えるようにしている. 後続へのoperatorへの値の送出タイミングの制御, Observableの処理スレッドの指定, タイマーのモックへの差し替えなどがSchedulerによって実現されている.
さてJavaScriptの場合, 原則的には単一スレッドの世界になる. Javaや.NETの場合とは違い, RxのSchdulerの役割は回り続けるイベントループ抽象となる. 永久に回り続けるイベントループの中で, どの時点で処理をdispatachするかがSchedulerの役目だ.
JavaScriptの世界にはメモリ空間を共有する方式でのマルチスレッドは事実上存在せず、アクター的なWorkerを立てるか(browser), 別プロセスを生成するかになるので(NodeJSには Web Workerっぽい何かを導入する案はあるが未確定), それぞれはシリアライズされたメッセージで繋ぐことになり, RxJSに隠蔽される形での縦断はできない. 同一のメモリ空間を複数スレッドで共有する方式の処理系でのRxならば「特定の重い処理をコールバックとして渡し, 別のスレッドに逃がしつつ, それらを上手く元のスレッドで待ち受ける」という処理をoperatorをつなぐだけで書けるが, RxJSでは兼ねてからの処理系の制約により, そんなカジュアルにスレッドをまたぐのは無理. 自前でObservableを返すRPC Adapterを実装すれば, それっぽいものはできると思うけれども, この場合はサーバーとクライアントをJSONで結んでいるのと大差は無い.
シングルスレッドなJavaScriptの世界ではRxのSchedulerは目立たない. 従来, JavaScriptの世界で並行性の問題に目立ってぶち当たることは少なかった. しかし, そこには目立たない・少ないだけで, 非同期操作とイベントループの狭間に並行性の問題は確かに存在する. いざ敵に現れたときに厄介な並行性の問題に立ち向かうときに非常に有効になるのが, このSchedulerである.
observeOn()/subscribeOn()
先にも述べたように, RxJSにおけるSchedulerはイベントループ抽象であるため, observeOn()
とsubscribeOn()
の挙動もイベントループに対する操作となる.
observeOn()
は, 後続のobservableに対して, 指定されたschedulerに基づいて値を流す. 一方, subscribeOn()
は指定されたschedulerのタイミングでsubscriptionを開始する.
違いは, observeOn()
はObservableがsubscriptionされている間に永続的に作用するのに対し, subscribeOn()
はsubscriptionが発生するタイミングそれぞれ一回のみに作用する.
つまり, 後続への値の創出を永続的にschedulerで制御したい場合はobserveOn()
を使用し, イニシャライザ内などで一気にすべてのObservableをsubscriptionした上で, 将来の任意の時点でsubscriptionを開始したい場合にはsubscribeOn()
を使用する.
前提として考えるよくあるケース
データフローを全てRx.Observable
に載せて書こうとする場合, Observableの中間地点で副作用を起こす必要がある場合が存在する.
たとえば, こんな感じ:
var value = ...; var sideeffect = observable.do(() => { value.increment(); });
このような場合, observableから伝播してくるpush 1回に対してvalue.increment()
が2度走ってしまうと困る. だが, sideeffect
をsubscribe()
した回数分だけsubscriptionのチェーンは構築されてしまうので, push 1回に対してsideeffectがsubscibeされた回数だけvalue.increment()
が実行されてしまう. さて, どうするか?
基本的な指針
- 複数回subscribeしても重複処理の無いようにHot Observableに変換する
- アトミックな処理区間を作る
- 状態の変化を適切に関係する値に反映させる
つまるところ, モナドっぽいものを作りましょう, みたいな話だったりする……
Hot Observableに変換し, 途中のObservableが(内部的に)複数回subscribeされることが無いようにする
RxはObservableをsubscribeした場合に , 内部的なsubscribeを繰り返し, Observableのチェーンを最初まで遡るようになっている(by design). これの内部的な経路開通が複数回別個に行われるか, 内部的subscribeの経路を共有しているかが, ObservableがColdかHotかの違いとなっている.このため, 何も考えない場合, 同一のObservableが複数回subscribeされるイコール同一の処理がsubscribeされた回数分走ってしまうので, 副作用を起こす処理が複数回走ってしまって問題になったりする.
これを避けるために, 同一のObservableが複数回subscribeしても一つの経路しか開通しないようにHot Observableに変換する. 具体的にはshare()
(publish()
+ refCount()
)とかする.
類似のアプローチとして, 終端であるsubscribe()
メソッドに渡したObserver(Subscriber)の中にのみ, 副作用を起こす処理を書くようにするというのもあるが, これをやりすぎると, Observableのchainが薄くなる一方で, subscribeがどんどん分厚くなっていく. これじゃ, ただのobserverパターンに逆戻りだよ!この手のアプローチを取らないと上手にハンドル出来ないケースも有るのだが, subscribeを厚くしなくてもハンドルできるケースは多いので, 末端のsubscribeに逃げずにハンドル出来ないかを最初に考えつつ動かすのが良いだろう.
なお, ObservableをHotに変換すればいいというものでもない. なぜなら, Hotでは最初のsubscribe/connect()以降に流れた値というのは取り逃してしまうが, Coldであれば, 新しくsubscribeする度にrootに向かってchainが走るため, 毎回初期値を流し込む必要のある処理を綺麗にObservableの中に閉じ込めることができる. パフォーマンスや従来のEvenEmiitterによるObserver patternを考えると, 防御的にHotに変換しつつ方が理解としては簡単だろう.
一方, 全部Coldで組んでおいて最適化の仮定でHotに変換していく方が, 迅速に動くものを作るという意味では簡単だったりもする(Hotに直してsubscriptionの数を減らす過程で, subscriptionの時系列問題が発生するので, 地味に面倒くさかったりするが……). 個人的な印象としては, 値を生成するジェネレータとしてObservableを使う場合はColdの方が良くて, UIイベントなどを取り扱う場合はObservableのchainがHotになるようにしたほうが総合して楽な印象.
アトミックな処理区間を生成するようにする
従来のJavaScriptだとそんなに意識することがないdata raceに関わる話. Observableを綺麗に小分けして組んでいるケースに問題になることが多い.
var a = event.map((id) => { return collection1.get(id)); }); var b = a.map((item) => { var val = item.barfoo(); return val; }); var c = b.do((val) => { collection2.set(val); });
ここで, a~cの処理はアトミックに一続きで処理される必要があるとする. その場合, 間に別の処理が割り込んできて, 変更対象のcollectionが変更されるとrace conditionとなって困る. 似た問題はRxに関係なく, 複数の処理が並行して動いていると発生する問題だが, 同期コードが多く, スレッドも一つしかなかったJavaScript界ではXHRのときくらいしか目立たなかった問題. NodeJSはfs.exist()
などの非同期APIで問題となっていたし, 最近だとPromiseが内部的にmicrotask queue(emca262だとjob queueだっけ)を積み上げるようになっていて, 時々目にするようになった(余談だが, これについてPaul TaylorはPromiseの設計ミスだと考えているようで, 「es-observable proposalでは, この過ちを繰り返したくない」と主張していた……).
さて, RxJSにおいて, 各operatorメソッドは必ずしも連続して実行されることが保証されているわけではない. 基本的には連続して動作するのだが, operatorが内部で使うスケジューラによって変更が可能であるし, observeOn()
などが途中に挟まれていると, その時点で保証は崩れることになる.
RxJS v5以降, 原則としてビルドインのオペレータは連続して(再帰的に)呼び出されるようにはなっているが, 先述した通り, observeOn()
などを間に挟んだObservableを返す関数を呼び出したりするケースでは信頼できないのは変わりない.
function barfoo(in) { // 次のイベントループになったら後続に値を流すような設定になったとする. return out.observeOn(scheduler.nextEventLoop); } var a = ...; var b = barfoo(a); var c = b.do(....); // この場合, cの実行は次のイベントループになる
各operatorに渡された各コールバックの単位のみがアトミックに実行されるのが保証されている単位であり, これ以上の単位については自分でschedulerを用意したり, そもそも操作対象のオブジェクトの操作が可能な範囲を限定することで回避するしかない. 他の言語であれば, 特定のスレッドに張り付けたり, ロックを上手に使ってアトミック操作が担保される区間を作ればいい. 要はそれとおなじことをやればよい,
一番わかりやすいのは単一のdo/map内で処理を完結させてしまう解決方法. operatorの使い方はセマンティックではなくなったり, 別のobservableを引っ張ってこようとした場合に, ちょっとややこしくなるという欠点がある.
もう一つの方法としては, クロージャやprivateメンバを用いて, 変数に触れる函数・オブジェクトを限定した上で, Schedulerを明示的に設定したり, Hot Observableとして結果を同時するなどして, 同時に操作する可能性のある外部要因を減らし, オペレータのチェーンが一続きに実行される, アトミックな区間を作成すること.
// そもそも分割しないアプローチ. var a = event.do((id) => { var item = collection1.get(id)); var val = item.barfoo(); collection2.set(val); }); // 関数内にクロージャ変数として閉じ込める function barfoo() { var collection1 = ...; var collection2 = ...; var a = event.map((id) => { return collection1.get(id)); }); var b = a.map((item) => { var val = item.barfoo(); return val; }); var c = b.do((val) => { collection2.set(val); }); return c.share(); } // privateメンバとして閉じ込める class BarFoo { private _collection1: ....; private _collection2: ...; result: ....; constructor() { this._collection1 = ...; this._collection2 = ...; var a = event.map((id) => { return this._collection1.get(id)); }); var b = a.map((item) => { var val = item.barfoo(); return val; }); var c = b.do((val) => { this._collection2.set(val); }); this.result = c.share(); } }
状態の変化を適切に通知するようにする
そもそも副作用を面倒くさい点とは何たるや?と言えば, ある要素の状態が変更されたという事実が, その要素の状態に依存している(処理の前提としている)後続の別の処理まで適切に引き継がれないために, 状態の不整合が積み重なっていくということにある. 古典的なオブザーバーパターンであれば, イベントを受け取った後に特定のフラグを変更し忘れたりするようなやつ.
同期pull型のAPIベースでプログラムを組んでいても, 現在の状態を観測するpull APIの呼び出し結果を下手にキャッシュして効率を改善しようとした場合などに, 状態の整合性の取り方を間違えるとバグる. それと同じ問題と言える.
さて, ここでバグるのは, 最初に述べた通り, 系としての状態の不整合が発生している中で無理に処理を実行していった結果, 状態がさらに崩れていくからだ. pull型と違ってpush型の場合, ある値が更新・変更された場合で後続に必要な情報を取得しなおす習慣があまりない, というよりも非同期を許容するpush型APIの場合, pushを受けた後に前提条件をpullで取りに行ったとしても, pushで飛んできた状態が生成された時点での状態である保証されていないので, 組み合わせにくいというのが表現として妥当かもしれない.
このような場合どうするべきかと言えば, 答えは単純. 状態変更が後続に伝わらないのが問題なのだから, 状態変更が後続に伝わるようにすれば良い. つまり, 後続の処理に必要となる値をpushで流せるようにすれば良いのであって
- 操作対象を独立したObservableとして, 状態が更新されると値が後続に流れるようにする(後続では
zip()
/combineLatest()
で待ち受ける) - 値を後続にpushするときに, 関連する状態をTupleか何かに突っ込んで一緒に後続に流す
のどちらかで解決する. 値の変更を伝える方法はいくつかあるが, Rxを用いて解決する場合は
- 状態変更をObservableとして発信するデータ型を作る(ライブラリ層として隠ぺいする)
- Map/Set/Arrayの派生型として, 値の挿入・削除・変更に対応してイベントを発行するものを作る
- Reactive Propertyのようにもう一段メタな型として用意する
- Subjectを使って, 明示的にonNextするのを頑張る
のどちらかとなる.
まとめ
- Rxが, というよりも並行して動作するプログラムの中で状態の整合性を取る話が軸になる
- Rxだとよく発生するというだけで, 別に今までも起こっていた話にすぎない
- Rx.Schedulerは目立たないが, これが有ることで泥臭い事例を上手いこと乗り越えていける