こちらの記事は2021年度の「株式会社カケハシ x TypeScript」アドベントカレンダーの1日目の記事になります。
fp-tsのEitherを利用した関数型プログラミングを基礎編と応用編の2つに分けて紹介します。
fp-tsについて
fp-tsはHaskell、PureScript、Scalaなどの言語でよく使われるような関数型プログラミングを補助する汎用的なデータ型、型クラスが用意されているライブラリです。
fp-tsでは「Monad」や「Functor」といったキーワードが登場してきますが、本稿ではそれらの概論については取り扱いません。今回紹介するEitherは前提の知見がなくても導入が可能であると考えています。目的に応じてライブラリのDSLを理解する程度の感覚でよいかと思います。
異常系についてデータ型を使って、合成容易な状態にしたい
上記コードは、シグニチャだけを見てもthrowされることが分かりません。内部の実装コードを見ることで初めて対象の異常系を確認ができます。実装内部のhowを知り、recover処理が必要な場合は、doSomethingのクライアントにてtry-catchする必要がありますが、型チェックできず、catch漏れなどが起因して障害に繋がることがあります。
関数型プログラミングは宣言型プログラミングの一種であり、シグニチャでWhatが分かることが重要です。
Eitherを使うとエラー内容を型で表現できます。以下にfp-tsのEitherの定義を抜粋します。
Eitherでは、一般的にLeftに異常値、Right値に正常な値を配置します。
TypeScriptにおけるEitherはUnion型によって構成されています。
既存のUnion型との違いは、Either型で利用できる多くの関数が用意されていてmapやflatten、concat、applyなどができることです。それらの関数を利用してEither同士の合成も可能です。
基本的な使い方はGetting started with fp-ts: Either vs Validationを閲覧してもらうとイメージがつくかもしれません。
基礎編
先程一部抜粋したEitherの定義と似た定義を自前で作成しているプロジェクトもあるかもしれません。正常系と異常系を含む関数の返り値をUnion型とし、クライアントで分岐する方法はTypeScriptにおいてもよく使われる手法です。便宜上、一部抜粋したEitherの定義のみのことをMinimalEitherと呼ぶことにします。
「MinimalEitherで組み立てた関数」と「Eitherで組み立てた関数」をそれぞれ比較していきましょう。
まず、具体的な比較の前に共通で利用するオブジェクトと関数を定義しておきます。
concat
[実現したいこと]
複数のEitherにおいて、RightをSumし、Leftになった場合に短絡でLeftを返す
MinimalEitherの場合
Eitherの場合
separate
[実現したいこと]
複数のEitherにおいて、RightとLeftに分割して配列で積み上げる
MinimalEitherの場合
Eitherの場合
このようにfp-tsに用意された関数を使うことによってボイラープレートなコードがシンプルに実装できることができます。適切かは分かりませんが簡便に表現すると、2つの値における非交和となる文脈において、Either型はUnion型の拡張とみなして利用して良さそうです。
次回の応用編では、PromiseとEitherの組み合わせを具体的なユースケースに落とし込んで説明していきます。