🦁
Functional Programming in C# まとめ 13章
12章のまとめは、こちら
13. Working with asynchronous computions
この章では以下の内容を説明する。
- 非同期計算を表すTaskの利用
- シーケンシャルと並列での非同期操作の合成
- Trabersable:コンテナ型のリストを扱う
- 異なるmonadのeffectを組み合わせる
13.1. Asynchronous computations
メモリ上で計算を行うのが、1秒だとしたら、一般的なI/O処理は、数か月から、数年に相当する。現実世界であれば、数か月もじっと待つようなことはせず、完了したときに通知してもらうだろう。これが、非同期計算の背後にある考え方だ。:処理を開始し、別の仕事に移り、その処理が完了したら戻ってくる。
C#4以来、非同期計算を利用するための主なツールはTask-based Asynchronous Patternだ。これは以下で構成される:
-
Task
とTask<T>
を使って、非同期操作を表す - awaitキーワード使い、Taskを待つ。これにより、カレンントスレッドが自由になり、他の仕事をすることができる。非同期処理が完了する間。
Task<T>
は、非同期のeffectを持つコンテナと考えることができる。
この考え方を示すために、Return、Map、Bind関数を定義する。
public static partial class F
{
public static Task<T> Async<T>(T t)
=> Task.FromResutlt(t);
public static async Task<R> Map<T, R>
(this Task<T> task, Func<T, R> f)
=> f(await task);
public static async Task<R> Bind<T, R>
(this Task<T> task, Func<T, Task<R>> f)
=> await f(await task);
}
13.2. Traversables: working with lists of elevated values
Traverseが定義される型はtraversableと呼ばれる。traversableの考え方を一般化する:
- Ts"traersable"な構造がある時、これを
Tr<T>
で示す - traverseで適用する関数
f:T -> A<R>
、Aは少なくともapplicativeである必要がある。 -
A<Tr<R>>
が欲しい
Traverseの一般化したシグネチャは以下
Tr<T> -> (T -> A<R>) -> A<Tr<R>>
- Tr:Option
- A:IEnumerable
とした実装例は以下:
public static Option<IEnumerable<R>> Traverse<T, R>
(this IEnumerable<T> ts, Func<T, Option<R>> f)
=> ts.Aggregete(
seed: Some(Enumerable.Empty<R>()),
func: (optRs, t) => from rs in optRs
from r in f(t)
select rs.Appnect(r));
以下、記事で紹介されている
「traverseを制する者はコードを制す」という動画が非常に参考になりました。
13.3. Combining asynchrony and validation(or any other tow monadic effects)
いくつかのeffectを組み合わせて使いたい場合にはどうするか、という議論。
結論としては、よく使うeffectをあらかじめ組み合わせたものを1つのmonadとして用意しておきましょう、ということ。たとえば、Task<Validation<T>>
など。
そして、ワークフローの途中に、たとえば、Option<T>
を返すような関数を挟みたい場合には、その結果を、Task<Validation<T>>
へリフトする。
Discussion