kotlin.connpass.comでコルーチンに関する話をしてきました。
Kotlin コルーチンを理解しよう
スライドはコチラ。音声や動画が無くても大体読めばわかる内容になっていると思います。
流れ
6月末にひつじさんからお手紙がとどく
直前の技術書典4でKotlinのコルーチンを使う前に自前で実装してみるという話を書いていたこともあり、 その辺をベースにした話の組み立てでできそうな気がするということでシュッと受けた。 結果的にめちゃくちゃ大変でしたが最終形はなんとかまとまったので良かったです。
調べたことや登壇時は触れられなかったことなど
話のベースは頭の中にあったものの、コルーチンの概念そのものが一体どういうものなのかあまり深く理解していなかったこともあり、その辺の調査もしなきゃな〜と手を動かし始めるとそのままどツボへ真っ逆さま。今回話の流れや時間的に触れられなかったことなんかをここにメモっておきます。
コルーチンとはなにか
昨年の6月頃に話したもうAndroidの非同期処理はasync/awaitでいいんじゃないかなぁと思ったでは"関数の途中で中断したり再開できるやつ"くらいにしか説明してなくて、さらにその辺は別に便利に使えるんだから気にしなくてよくないくらいの気分でいたんですが、しかしそれはそれとしてちゃんと調べるべきだよな〜と思ってとりあえずWikipediaなんかを見つつ、メルビン・コンウェイの論文が初出らしいってことで論文読もうかとおもって論文を読んだ。
Design of a Separable Transition-Diagram Compiler
発表にもある通りCOBOLコンパイラの話で、コルーチンを使っていい感じにCOBOLコンパイラを記述できるぜ!がメインの話なので、コルーチンそのものは現在の説明からはかなり遠い記述になっていた。この論文では字句解析と意味解析を別モジュールで実装することで分離したプログラムがそれぞれ協調して動くということを実現しているらしい。
その後コルーチンに関する説明を調べると「協調的マルチタスク」と「プリエンプティブマルチタスク」という用語によって両者を比較する文脈があらわれるのだけど、これはコルーチンがスレッドどころかプロセスすらない時代に生まれたマルチタスクの概念で、コルーチンが論文に登場したのと同時期に「タイムシェアリング」についての研究も盛んになっていて、結果的にタイムシェアリング勢によるプリエンプティブマルチタスクとかスレッドとかが主流になってコルーチンは廃れたみたいな流れがあってそれはそれで面白いんだけどそれ道具としてのコルーチン関係なくない?みたいになってどう触れるか迷いが生じたりした。
協調的マルチタスクというのがコルーチンの重要なコアの概念なんだけどいかんせん一番シンプルな例でもproducer consumerパターンでややこしく、async/awaitで今まさに価値が見出されつつあるコルーチンの説明の頭にもってくるのしんどくないってなってそこも却下になった。どっちかっていうとKotlinコルーチンにおけるproduceコルーチンビルダー関数やChannelがまさに本来のコルーチンって感じだけど実際利用するケースってかなり少ないので、コルーチンを使っていこうなと言うにはニッチすぎて厳しいと考えて協調的マルチタスクというワードは出さないようにすることにした。もうひとつややこしいのは、実際のKotlinのコルーチンはスレッドプールで動くという点があってそうすると協調的マルチタスクとプリエンプティブマルチタスクの対比がふんわりしてしまって簡潔に説明できないよなというのも悩ましい点だった。その辺の観点からFiber、グリーンスレッド、軽量スレッドとかにも触れるのはやめた。
2004年の論文にRevisiting Coroutinesというのがあって、ここで「昨今はマルチスレッドプログラミングが盛んになってコルーチンをnativeサポートする言語が減ってきてけしからん。コルーチンはいいぞ」って書いてあって元気だなって思って読んでみると、現代に通じるコルーチンの分類をしているぽくて我が意を得たりと思ったのだけど内容が難しくて、完全非対称コルーチンが多分async/awaitだよなーと思いつつでも言明はされてないしワンショット継続とかのワードもよくわからず(限定継続のこと??)、これをもって現代のコルーチン実装が整理されたとは言えず参照を断念した。
英語のWikipediaのコルーチンの説明が一番端的で正しいのだけど、
コルーチンは、サブルーチンを一般化するコンピュータプログラムコンポーネントです。 特定の場所で実行を一時停止および再開するための複数のエントリポイントを許可することによって、非プリエンプティブマルチタスクを実現します。
みたいな文章が初っ端にあって、確かにいろいろ調べた今となってはこれでわかるんだけどさーこれ参照した上で説明すると終わらねーだろ〜ってなって、その後の例やサブルーチンやスレッドとの比較は良かったので取り入れたものの実際に書いてる事よりかなり平易に説明した結果厳密ではなくなってそこはそれで胃が痛い気持ちだったりした。サブルーチンを一般化するというのは同じスレッドで動く子コルーチンが中断しない場合サブルーチンと同じ動きやで、みたいなことでコルーチンですべて表現できるよねということなんだけどそこ説明するのはその後の話とは関係ないからしんどいなどなどしんどかった。
そこで55年飛ばすことにした。
最終的にはコルーチンを端的に表す文言としてCoroutines for Kotlin (Revision 3.2)での説明と日本語版Wikipediaの利用用途についてを参照することになった。公式リファレンスの説明も検討したがこちらは道具としてのコルーチンの話が厚めだったのでやめた。この話のあとにする話と関連していてかつ破綻がない情報を抜き出すのはかなり骨が折れた。今回の取り出し方は本当にギリギリだったなぁと思う。
この辺は本番2日前辺りでようやくfixした。
Kotlinはどのようにコルーチンを実現しているのか
ここについてはすでに大体調べていたのでそこまで迷いはなかった。 昨年のKotlin Confの発表を見返しつつ、CPS Transformとかステートマシンについて再確認して構成を落とし込んでいった。
- Deep dive into Coroutines on JVM https://www.youtube.com/watch?v=YrrUCSi72E8
- Introduction to Coroutines https://www.youtube.com/watch?v=_hfBv0a09Jc
時間的な問題やあるいは後半の使い方に直接関連しない点で削いだ部分が結構あってそれはそれで惜しい気はしている。実際にバイトコードを読みながら、ステートマシンとして生成されたクラスがCoroutineImplを継承していたりLambdaを実装していたりlabelとswitchで状態を分解している様子を眺めたり、except宣言された(suspend R.() -> T).createCoroutineUnchecked関数の実装と継続インターセプターが使われる様などを眺めたりしたがったが、収拾がつかない気がしたのでやめた。
suspend修飾子、継続インタフェース、CPS、コルーチンビルダー、継続インターセプターの説明は最初から入れていたが、前半のコルーチンとはなにかの説明で出たコルーチンの要素と整合する形で落とせたと思うのでよかった。が、コルーチンビルダー、継続インターセプターは完全にライブラリ実装に近い話なので基本的な使い方の文脈で説明してもよかったかもと思った。
Kotlinコルーチンの基本的な使い方
前半に力を持っていかれ、本当に基本的な部分しか触れられなかったのと練習不足でわりとグダグダだったな〜と反省するなど。ただ前半の説明を踏まえておけば最低限でも自分で進めていく力を得られると思って使い方部分はかなりあっさりにした。Guide to kotlinx.coroutines by exampleの内容を踏襲したほうがいいんじゃないかと思った瞬間もあったがrunBlockingを混乱なく説明できない気がしてやめた。
コルーチンの前提知識を得た状態とはいえストーリーとしてもっとライブラリ実装側の話とか提供されている機能群を厚めにしないとふわっとしないかと思いつつ基本形を作ってasync/awaitのバリエーションという形となった(手が回らず)。ジェネレータ、Channelやアクターの話、テストに関する話なども入れられたらなぁと思いつつ、リハしたら時間オーバーしたのでそこは手はつけなかった。
結構ギリギリで、最初はlanch関数の引数を上から順に説明したりしていたが当日スライドを見返しながら調整したりした。Jobの説明とか入ってなかったり。
とか言っていたが、この章のスライドは発表の2時間前までいじっていた。終盤のRetrofitのDeferredや、EventBus、android-coroutinesの話は全然深掘りできなかったのでちょっと残念だけどおもしろ感を出したかったので割り切り。
前日自宅でリハしたあと奥さんに「(血の気が引いて)顔が緑だよ」って言われて「グリーンスレッドだけにね」って言ったかどうかは定かではない。
当日の様子
ランチ難民をしていたがひつじさんがコーディネートしてくれて、元同僚と初対面の方との3人で行ったのだが、 初対面で知らない人と思っていたらなんとAndroid アプリ設計パターン入門で僕が抜けた穴を塞ぐべく参加して下さった吉岡さんだとわかり「その節はどうも」みたいな感じになった。お礼を言えてよかった。
寿司の様子。というかこの1枚しかこの日は撮ってなかった。
スピーカー控室はめっちゃでかくてスピーカーが10人掛けテーブルを1人で専有しポツポツ座ってるみたいな感じで快適だった。 プロジェクターがありスライド映して試せるのかと思ったらA会場のライブが始まりビビったが面白かった。
会場ウロウロしたり控室戻ったりスライドいじったりコーヒー飲んだり自家製プディング食べたりして過ごした。 いつもは緊張でほぼ他の発表見られないのだけど控室にライブが流れるのでよかった。
本番後はAsk the Speakerで幸いにも行列ができいろいろ会話した。 RoomにDeferredくるか!?
Twitterの感想も好評のようでよかった。 懇親会ではAndroid老人会が発生して面白かった。
出し殻のようになって翌日はオクトパストラベラーをプレイし続ける一日となった。
良いもの残せたのではないかなと思うし運営も会場も最高だったのでめっちゃ良かったと思います(小並感)。参加して下さった方々ありがとうございました。