visible true

技術的なメモを書く

Kotlin Fest 2019で「Kotlinコルーチンを理解しよう 2019」を話してきました

Kotlin Fest 2019楽しかったです。 今回は「Kotlinコルーチンを理解しよう 2019」を話してきました。 資料作りの様子、各セクションを作る時になに考えてたか、反省点などまとめます。

f:id:sys1yagi:20190826160412p:plain 前回と今回でロゴの形がちょっと変わってる

資料

コルーチンとはなにか、から実際使ってどうテストするかまでを45分でまとめるのはなかなか難しかったですが、一応網羅的にやれたか!?とは思っています。作る時はヒイヒイ言っててなにも考えてなかったけど改めてみると、これから触っていく場合に最初に読むものとして結構いいかもしれんと思うなどしました。 speakerdeck.com

コルーチンとはなにか、なにがうれしいのか

概念については自分のなかではある程度理解できてたものの、対象コルーチンや非対称コルーチンについて曖昧だったので改めて調べた。 新雑誌「n月刊ラムダノート」の『「コルーチン」とは何だったのか?』の草稿を公開します - まめめもはとても参考になりました、また、Google公式で紹介していた次のエントリも考え方の整頓にとても助かりました。

特にパート3のSingleRunnerの実装はこの辺に転がっているがめちゃんこ役に立つので参考にすると良いと思います。 このほか2004年の論文 Revisiting Coroutinesもまた改めて読んだ。前よりは理解できた。

コルーチンの何が嬉しいかについては昨年末のGoogle Play App Dojoでの登壇(http://sys1yagi.hatenablog.com/entry/2018/12/19/104023)でまとめていたのでそこまで迷いはなかった。

スライドの最初の、コンウェイによる概念の発明、simulaによる実装、様々な言語の様々な実装の話は、 発表では30秒くらいでサラッと流したけど、コンウェイの論文(http://www.melconway.com/Home/pdf/compiler.pdf)を見に行ったり、simulaのコルーチンプログラムを読んだり(http://staff.um.edu.mt/jskl1/talk.html の Chess game control using Two Masters approach.のセクション)、Module-2やSchemeLispに入門してみたり、Pythonのasync/awaitやRubyのFiberを試したりなんかしてめっちゃ時間をかけてしまった。が良かった。対象コルーチン、非対称コルーチン、各言語の実装、継続やスタックがどうとかいう議論など、歴史を感じる事ができた。これのおかげでサラッと話せたんだなと思う。

Kotlinにおけるコルーチンの仕組み

最初は結構細かく書いていた。suspendキーワードの話のあと、CPS(Continuation Passing Style)とステートマシン生成について話し(https://github.com/Kotlin/KEEP/blob/master/proposals/coroutines.md#implementation-details )、さらにsuspend関数がContinuationImplに、suspendラムダがSuspendLambdaに変換される様子をバイトコードを見て話したり、更にsuspendラムダの拡張関数を使ってコルーチンを実行する様子なんかも眺めたかったけど、実際使っていくにあたってはそこまで意識しないことだよなーっていうのと圧倒的に時間オーバーする事がわかって削った。

// suspendラムダの拡張関数。関数型に拡張関数生やせるの面白い
@InternalCoroutinesApi
public fun <T> (suspend () -> T).startCoroutineCancellable(completion: Continuation<T>) = runSafely(completion) {
    createCoroutineUnintercepted(completion).intercepted().resumeCancellable(Unit)
}

結果的に利用者にとって一貫性のある内容になったんじゃないかって気がしたので良かった(使う時にCPSが〜とかはあんま考えないので)。

Kotlinコルーチンのきほん

Kotlinコルーチンは関係要素が多すぎて何から説明するかとても大変だった。このセクションは基本的にKotlin公式のドキュメントに沿って話すことにした(https://kotlinlang.org/docs/reference/coroutines/coroutines-guide.html)。このセクションが一番長くて時間が足らないってことで結構削ってしまった。コルーチンビルダー関数について深ぼったり(特にsuspendラムダとか起動周りとか)、コルーチンスコープのインスタンス作って動かしてみる例があればもっと理解を深められただろうと思う。コルーチンディスパッチャーについても、こう使うと切り替えられますという感じで割り切ったのでちょっと心残りである。コルーチンディスパッチャーは奥が深くてDefaultとIOの違いとか、MainとServiceLoaderに関する話なんかも面白い。実行コンテキストの合成とかもややこしいけど面白い点だ。あともうちょい、このセクションに出る要素でほぼ実用はできるって点はもっと丁寧に説明できたらな〜と思ったりした。

コルーチンスコープと構造化された並行性

自分のなかで構造化された並行性についてはイメージがあったのでわりとスムーズにいった。最初はRoman氏のStructured concurrencyとそこから 参照されているNotes on structured concurrency, or: Go statement considered harmfulに触れたり、構造化プログラミングの非同期版やで〜みたいな話をしようと思ったけど、生まれた背景をそこまで細かく知る必要は無い気がしたので省略した。coroutineScope関数のあたりで、実はwithContext関数もスコープビルダーだよとか、ファストパスでwithContext関数呼び出しがネストしても実行コンテキストが変わらなければそのまま実行するよとか話したかったけど省略した。またSuperVisorについても省略した。viewModelScopeはSuperVisorJobを使ってたりするけど、まぁ通常はほぼ使わないと思うので省略。 分岐と合流の図は自分で書いてみてマジわかりやすいと思ってたので、わかりやすいという感想を見かけてやったねってなった。

コルーチンと設計

ここもまたサラリと書いた形になった。掘り下げようと思えば色々できたのだけど、考え方だけ伝えればいいかってことでだいぶ削った。そういえばスコープのモチベーションとリークの関係についてもっと説明するべきだった。その上でプラットフォームでスコープ提供してて最高って話ができると良かったかなと思う。suspend関数をメインセーフティで実装したり、コルーチンをメインセーフティで起動したりはまぁこんなものかな。メインセーフティでない例をしっかり示せてなかった気がするのでそこはちょっと反省点。

コルーチンのテスト

コルーチンのきほんのセクションと同等くらいの大きさでめっちゃ大変だった。 テスト方法についてもまだまだ固まってない部分もありどうまとめたもんか迷ったりした。 Githubにもぜんぜん実践的なテストコードが転がってなくて結構途方に暮れたが自分のアプリで徹底的にテスト書いて事なきを得た。間接的なコルーチンの呼び出しについて、ディスパッチャーを置き換える具体的な方法の紹介はあったほうがよかったなと思った。また、runBlockingTestやsetMainやRule周りも結構ザーッと書いてるんでもうちょい補助的な情報を示せたはずと思ったりした。準備の中でテストに関しての理解が間違ってた!みたいなことが数回発生して全練り直しを2回くらいして大変だった。TestCoroutineDispatcherとDelayHandler周りの実装は面白いので覗きたかったけどさすがに時間が足らないので諦めた。テスト周りは多分まだまだ書く度に悪戦苦闘することになりそう。

おわりに

今回準備にあたってコルーチンに関する論文をいくつか読んだ(ざくっとだけど)らとても面白かったのと、最近のCoroutines FlowでHoareのCSPモデルが関係する話( Kotlin Flows and Coroutines - Roman Elizarov - Medium)を読んだりして計算機科学をしっかり学んでみたいな〜大学院とかがいいのかな〜とかなんとか思ったりした。 しかしまずは車の免許を取ろうと思っている。そしてキャンプに行きたい。

参加者、スタッフの皆様ありがとうございました!引き続きKotlinを愛でていきましょう!