rider.お品書きサクッと書いて公開して、後でちまちま修正する事がママあります。なんじゃこりゃ?と思ったら、数日してから見て貰えると嬉しいです。「スクラムの実践方法を分かりやすくマンガも交えて解説。Linking」という題目で、NL名古屋で登壇してきました。NとLの字さえ使っていれば何でも良いというカオスなショートセッション会なのですが、内容は非常に濃くて面白いものばかりでした。満足度が高かったです。Togetterのまとめはこちら今回写真撮り忘れてしまったのでありませんが、50名超えと大入りな感じでした。Asyncのシームレスな相互運用を行うネタを発表してきました。このネタで行くにはまだ「ひよっこ」で恐怖しかない感じでしたが、掴みもスベる事なく発表できたのでうれしかったです。一部の方には触れるものがあったようで、「おおーー!!」という声はやって良かった感ありました。発表はショートセッションですが参加者が多いこともあり、最終的に時間制約が10分となって苦しいところでしたが、予定通り進行を無視して「継続」させていただきましたありがとうございます。補足さて、内容は駆け足だったので少し補足しておきます。FSharp.Control.FusionTasks.FS40検討アイデアはすぐに思いついたものの、実現可能かどうかが良くわかりませんでした。Asyncクラスは似ているな、もしかしたら簡単に相互運用できるかも?と思ったのがきっかけです。2.0から実現している)。TaskはF#に依存しないように改めて設計しなおしたのだと思われます。F#非同期ワークフローは、AsyncBuilderクラスを使用する、一種の構文糖(computation式)だと理解しています。Taskを扱えるのではないかと考えました。Asyncで使うことが面倒であったということがあります。Taskのためのサポートはあります。「Async.AwaitTask関数」がそれですが、この関数は見ての通りT型が特定されなければならず、非ジェネリックTaskをAsync<unit>に変換できません。逆の変換(AsyncからTask)への変換方法もありません。逆の変換ができると、F#で書いた非同期ワークフローのインスタンスを、C#側でawait出来るようになるため、さらに応用性が広がります。computation式については、「詳説コンピュテーション式」が詳しいです。持ち出した「タスクコンテキスト」という用語なのですが、もはや統合されたこの世界では「非同期コンテキスト」としか言いようがないですね。「非同期コンテキスト」をシームレスに結合した場合、維持されなければならない重要なポイントがあります。それは、「同期コンテキスト(SynchronizationContext)」と「キャンセルトークン(CancellationToken)」です。Task側の事はある程度わかっているのですが、F#非同期ワークフローではこれがどのように維持されるのかがわからず、シームレスな相互運用が実現するにはココの担保が不可欠だと考えていました。fsugjp!!)F#にはAsync.FromContinuations<T>関数があり、これを使うとコールバック関数を経由してAsyncクラスの結果を制御できます。これは丁度TaskCompletionSource<T>クラスを使ってTaskを間接的に操作することに相当するので、Taskの結果を反映させることに使えます。余談ですが、初見ではあの引き数で何ができるのか、さっぱりわかりませんでした(つまりビギナー認定w)「Continuations」という単語の響きに導かれ、逆のパターンにはAsync.StartWithContinuations<T>関数を使えばよいことがわかりました。こっちはもっと単純で、正常・異常・キャンセルの継続処理をCPS形式で渡すだけです。同期コンテキスト自体は、Asyncクラス内で非同期待期に使用しているので、F#非同期ワークフローで使う限り特に問題なさそうです。Task.ConfigureAwaitメソッドに相当する同期コンテキストのキャプチャ制御についてはむしろF#の方が柔軟性があり、Async.SwitchToContext・Async.SwitchToThreadPool・Async.SwitchToNewThread関数を使用して、いかようにでも同期コンテキストを操ることができます。キャンセルトークンの方ですが、F#側はAsyncクラスのインスタンスを何らかの方法で実行するときにトークンを保持し、それがAsyncクラスの非同期コンテキストの情報として保持されて使われます。RunSynchronouslyは説明のために使用しています。本当は使わないように書くべきです。Task.Waitに相当します。上記のような状態でSleepしているときにトークンがシグナル状態となると、正しくSleepが中断されます。Task.Delayの場合はキャンセルトークンを受け取るオーバーロードを使用していないと中断できませんが、F#非同期ワークフローの場合は、現在の非同期コンテキストに伝搬するトークンが管理され、Async.Sleepはそれを監視しているので正しく中断されるのです。プロパティで管理されていて、Async.Sleepはこれを参照しているからです。実際、Task.Delayと異なり、Async.Sleepにはトークンを明示的に指定するオーバーロードはありません。作ってみた感想この部分、私的にはとても良くできていると思いました。いうのも、F#非同期ワークフローの「非同期処理」に関する範囲と制約は、すべてワークフロー(computation式)内に閉じているからです。キャンセルトークンの管理もここで閉じているので、操作する側はAsyncクラスのインスタンス=非同期コンテキストと言うように、はっきりとイメージ出来ます。async-awaitではこれが曖昧で、ビギナーにasync-awaitの事を説明するためには、どこからが非同期コンテキストなのかをしつこいほどに解説する必要がありました。デフォルトではキャンセルに対する操作は完全に使用者任せとなり、Task.Delayのようにトークンを引き渡すシグネチャを明示的に設計に盛り込む必要があります。私はこのことに気が付くのが遅かったために、フレームワークインターフェイスの変更という大手術を行う羽目になったことがあります。周到に準備しても、使用者がトークンを渡すのを忘れると台無しとなり、トークンをどこで管理するのかということも(使用者が)考える必要があります。Taskとasync-awaitが後発でありながらこういう選択をした理由も恐らくあるのでしょう。想像出来ることと言えば、async-await方式は、メソッド内のコードに「await」という単語が入るだけで、それ以上の大幅な変化がないと言うことです。実用的な非同期コードをasync-awaitで書いたことがある方ならわかると思いますが、現実には非同期であることを意識したコードを書かないと、pitfallに落とされます(一例を例外の記事でも書きました)。F#非同期ワークフローでは、「async」に囲まれたcomputation式内でしか使えず、computation式では「let!」「do!」などの普通には使わない予約語を使う必要があります。上で述べたような問題とのトレードオフとしては悪くない選択だなと思います。説明も簡単なのが大きいですね。Asyncクラスはそれぞれ役割もインターフェイスも似ているにも関わらず、現実には設計思想からして全く違うものだという印象が強くなりました。後は、現在進行中のもう一つのプロジェクトと合わせて、何とかF#のコードが書けるようになったと言うことかな。人に説明するにはまだいろいろ足りてない感じですが、F#や関数型言語の面白さの一端は見えてきました。WordPress%d人のブロガーが「いいね」をつけました。