Download - continuatioN Linking
自己紹介
けきょ (@kekyo2, www.kekyo.net)
ロードバイク乗り
Microsoft MVP for Visual Studio and Development Technology
認定スクラムマスター・スクラムプロダクトオーナー
Center CLRオーガナイザー
アジェンダ
CPS (継続渡しスタイル / Continuation Passing Style)
.NET Task / async-await / F# Async workflow
Continuation Linking
キーワード: Continuation Passing Style「継続渡しスタイル」◦ 継続渡しスタイル (CPS: Continuation-passing style) とは、プログラムの制御を継続を用いて陽に表すプログラミングスタイルのことである。(Wikipedia)
何だ、名古屋的ヤヴァイやつか。
… 陽に表す、とは
Continuation Passing Style
コールバックで処理を継続させるといえば、JavaScriptのコールバックハンドラーとかメジャーですね。
「継続渡し (Continuation Passing)」
難しくない!!「CPS」という名前を付けただけだ!
Continuation Passing Style
CPSについてわかりましたか?
これが何?という疑問は「沼」なので、ここではスルーします。◦各自、ググって下さい。
「継続渡し」というものが感覚的に分かればよいです。
アジェンダ
CPS (継続渡しスタイル / Continuation Passing Style)
.NET Task / async-await / F# Async workflow
Continuation Linking
キーワード: .NET Task
.NET 4.0から導入された、「タスク」を透過的に扱うクラス。
更に、.NET 4.5とC# 5.0のasync-awaitを使って、非同期処理をタスクとして扱えるようにした。
CPSと何の関係が?
.NET Task (.NET 4.0 / C# 4.0)
まずは、.NET 4.0 TaskのCPS (ContinueWithを使う)
関数に直接継続を渡していないが、Task.ContinueWithに継続を渡している≒ CPS
注: 手を抜いてasync使ってます
.NET Task (.NET 4.5/C# 5.0)
そして、.NET 4.5/C# 5.0 でのasync-awaitによる非同期処理
これが「継続」だと分かりますか?
async-awaitでもTaskを使う
.NET Task comparison
つまり、async-awaitによるTaskの継続も、「構文糖」と見なせば一種のCPSです。
…たぶん
これが .NET 4.0 (C# 4.0 Task only) こうなる .NET 4.5 (C# 5.0 async-await)
F# Async workflow
ほとんどasync-awaitと一緒これが「継続」だと分かりますか?
返されるのは Async<int>
※ let! (Let-Bang) は、Async<T>を非同期処理として待機して、結果のTを束縛します。C#でのawaitに相当。
※ F# Async workflowのCPSは長いので省略。Async.FromContinuations<T>関数を使います。
非同期継続のまとめ
.NET Task : ContinueWith
.NET Task : async-await
F# Async workflow : let!
それぞれの非同期処理の継続手段が、暗に「CPS」であることがわかりましたか?
アジェンダ
CPS (継続渡しスタイル / Continuation Passing Style)
.NET Task / async-await / F# Async workflow
Continuation Linking
「継続」を「接続」する
.NET TaskとF# Asyncをシームレスに接続して、相互運用可能にできたらいいね。◦大枠において対比出来る、Task<T> ⇔ Async<T>という関係から妄想
つまり:◦ F# AsyncをC#側でawait出来たらいいよね?
◦ .NET TaskをF# Async workflowでlet!出来たらいいよね?◦ HttpClient.GetStreamAsync()とか、普通にuse!できるようになるよ。
.NET Task / C#側のシナリオ
Async<T>クラスに対して拡張メソッドを定義して「AsTaskメソッド」を生やし、 Async<T> →Task<T>に変換可能にする。◦ Async.StartWithContinuations<T>関数を使って、TaskCompletionSource<T>を操作(SetResult・SetException・SetCanceled)し、Task<T>クラスを返す。
Async<T>クラスに対して拡張メソッドを定義して「GetAwaiterメソッド」を生やす。◦ C# でAsync<T>に対して直接awaitが可能になる。
※ F#のAsync<T>クラスは、正確にはFSharpAsync<‘T>クラスです。
F# Async workflow側のシナリオ
Asyncクラスに対して拡張関数を定義して「AsAsync関数」を生やし、Task<T>→Async<T>に変換可能にする。◦ Async.FromContinuations<T>関数を使って、Task.ContinueWithメソッドのコールバックからAsync<T>の継続を操作する。
AsyncBuilder<T>クラスに対して拡張関数を定義して「Source関数」を生やす。◦ Source関数がTask<T>を受け取りAsync<T>に変換することで、F# Async
workflow内で直接let!が可能になる。
※ let!の他にdo!・return!・use!もありますが、Source関数を定義することでこれらすべてに対応できます。
こまごまとしたシナリオSystem.VoidとFSharp.Unitの、型安全な変換の考慮◦ Async<unit>をawaitすると、戻り値を受け取れないようにする。◦ Task(非ジェネリック)は、do!しかできないようにする。
CancellationTokenのサポート◦ F#側はAsync workflowのコンテキスト中で共通に割り当てられるTokenが存在するが、Task側にはそのような概念はない。
◦ AsTask/AsAsyncで変換する際に、明示的に渡せるようにする。
Async.RunSynchronous関数を使わない◦ 使ったらハードブロックしてしまう。Task.Waitメソッドを呼び出すことに該当。FromContinuations<T>やStartWithContinuations<T>を使う。
AsyncCompletionSource<T>を作る◦ FromContinuations<T>はコールバック関数を基礎としているので扱いにくい。
TaskCompletionSource<T>に相当する、F# Asyncの委譲クラスが必要。
ご清聴ありがとうございました!
これで実装してます→ GitHub: FusionTasks◦ https://github.com/kekyo/FSharp.Control.FusionTasks
◦ https://www.nuget.org/packages/FSharp.Control.FusionTasks.FS40/
スライドはブログに上げます◦ http://www.kekyo.net/
※ Thx: JavaScript プログラミング解説http://so-zou.jp/web-app/tech/programming/javascript/event/handler/