Posted on 2014-05-12 tags: haskell, scala, future
クックパッド社で行われていたすごいHaskellたのしく学ぼう輪読会 の完走を記念して関数型LT大会という会が開催された。クックパッド関係なく関数型に興味ある人が集まってワイワイする感じっぽかったので、ひとつなにか話して叩かれに行ってみるかと思って発表してきた。そういえばクックパッド社に行くのは2回目であった。
Scala で初めて Future という概念を知ったのだけど、触り始めの頃はどういうコードを書けばいいのかも全然良くわからなくて苦しんでいた。ふと「これって Haskell の IO と同じじゃね?」と気づいてから急に視界が開けた気がしたので、そのへんのことを話してきた。ググっても「Future と IO 似ているよね」という説は全然見つからず、「いや全然似てねえよバカ」とかマサカリが飛んでくることも覚悟していたけど概ね生暖かく迎えられたようで安心した。
後で @eagletmt 氏に「あれってモナドだって言ってるだけですよねえ」と突っ込まれたのでそのへんの話を追記した。たしかにモナドの特徴を言っているだけだが、重要なのは基本的に Future は一方向性のモナドであるということで、気軽にブロックして中身を取り出すようなことをやってはいけないのである。 Twitter の
Effective Scala にもそのへんことが書いてあるし、
Finagle について書かれたドキュメントを見るとブロックするなと書いてあるのが頻繁に見つかる。
そのへん理解してないんじゃないかなーと思うコードに以前苦しめられた過去が
ある関数内で Future を呼んでいるのにその戻り値が Future[A]
というシグネチャになっていない関数 (こういう関数を簡単に書けてしまう) というのは、よほどのことがない限りアンチパターンである。具体的に許されるのは次の2パターンくらいしかないんじゃないかなーと思っている。
1 は Future 内で行われる IO 処理を「投げっぱなし」にすることで、その IO 処理内で適切にエラーハンドリングされており自分でエラーハンドリングを書かなくていいと確信できる場合である。その場合は途中で呼んだ Future を投げっぱなしにして、非同期で IO 処理だけさせてメインスレッドは次に進むということをやって良い。
2 は必要な情報が全部は集まっていないけどとりあえずレスポンスを返さなきゃいけないような場面において、呼んだ Future がタイムアウト前に返ってこなければ見捨てるというもの。ある意味同期処理なわけで、基本的にはやってはいけないがタイムアウトという条件が付いているなら話は別だ。タイムアウト時間とそのイベントハンドラを登録し、それが呼ばれた時点で対象の Future に対してそれが完了しているか否かを問い合わせ、完了していれば中身の値を取得して同期的に次の計算に移ることになるので Future
の文脈は消える。戻ってこなかった場合はその Future をキャンセルしておく
(com.twitter.util.Future
では raise(new FutureCancelledException)
を呼ぶ)
のがたぶんマナー的に正しい。
twitter/util はこのへんうまく出来ていて、呼んでいる Future が戻ってきたら処理を続けるとか、あるいは同時に呼んでいるどれかが戻ってきたら処理を続けるみたいな、イベント駆動な設計になっている。 select
とか epoll
といった UNIX
システムコールに関連した話になってくるわけだが、 com.twitter.util.Future
には
select
と poll
という名前そのままの API がある。
scala.concurrent.Future
にはその手の話はないっぽい?システムコールレベルではまだあまり使ったことがないので触ってみないと…
勉強会での発表というものは自分が勉強するためにあるものであって、自分が勉強したことに比べれば話す内容は他人にとってもわかりやすくするものであると思っていた。だが他の人達の発表を聞いていても意図的にレベルを落としたように感じられるものはなく、むしろみんな全力で準備している感じがしてすごかった。「それ知ってるよ〜」という類のものはほとんどなかったし、自分の到底理解の及ばない型レベルガチ勢の方々の発表もわかりやすくて面白かった。 LLVM や TaPL, さらには圏論が一般教養として扱われていた感もあり関数型界隈はレベル高いなあと思った。
@rejasupotaro さんとは以前 第7回若手Webエンジニア交流会で少しだけ話したのだが、昨日はより突っ込んだ話をした。 Twitter のオープンソースのコードを読んでいると、 Scala の標準ライブラリに比べてケチくさい最適化を頑張っていないように見えるとか、 Twitter をしてもまだ Netty 4 に乗り換えられない感じなんだろうなあとか。結論としてはジャバこそが実用的な言語ということになった。
レジャスポ氏に「時代はジャバだ」と力説してしまった #functionalLT
— matsumoto (@daimatz) May 11, 2014
帰りで少し一緒になった @taiki45 さんともそんな話をした。もうちょっと我々のアプローチがどの程度どうなのかという話とか、他のカイシャさんでそのへんどうしているのとかいう話を聞きたい気もした。
あとは、今回も Twitter でしか見たことのなかった何人かの人達の存在を確認した。
twitter/util と twitter/finagle 読もうと言いつつ、いやそこそこ読んではいるんだけどどうしても finagle のタスクスケジューリング周りがつかめていなくて、困っている。標準版の Future は FuturePool を直接触る設計になっていて直感的だが、 Twitter
版の Future は finagle を普通に触っている限り FuturePool
というかタスクスケジューラが一切表に出てこない。裏で謎スケジューリングしていたり、かといえば普通に ExecutorService
を作っているところもあったりで、結局どこが実態なのかわかっていない。一人
finagle コードリーディングでも記録に残していくかなーと思っているところ。関連して twitter/util と netty もある程度読むことになりそう。