🦁

Functional Programming in C# まとめ 13章

2024/03/15に公開

12章のまとめは、こちら

13. Working with asynchronous computions

この章では以下の内容を説明する。

  • 非同期計算を表すTaskの利用
  • シーケンシャルと並列での非同期操作の合成
  • Trabersable:コンテナ型のリストを扱う
  • 異なるmonadのeffectを組み合わせる

13.1. Asynchronous computations

メモリ上で計算を行うのが、1秒だとしたら、一般的なI/O処理は、数か月から、数年に相当する。現実世界であれば、数か月もじっと待つようなことはせず、完了したときに通知してもらうだろう。これが、非同期計算の背後にある考え方だ。:処理を開始し、別の仕事に移り、その処理が完了したら戻ってくる。

C#4以来、非同期計算を利用するための主なツールはTask-based Asynchronous Patternだ。これは以下で構成される:

  • TaskTask<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));

以下、記事で紹介されている

map を使いこなせるあなたに traverse

「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