ジェネリックなFunctorとMapper
あらゆるコンテナに対応する汎用的なmap関数を記述します。
「ジェネリックなFunctorとMapper」はCoddyKit上の無料TypeScript Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはTypeScript Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 TypeScript Academyコースには全4レッスンが含まれています。
汎用Functor
ここでは、このエンコーディングを本来の目的に使います。登録済みの任意のコンテナで動作する汎用mapを作ります。URIをパラメーターに取るFunctorインターフェースで、1つのメソッドを宣言します。
interface Functor<F extends URIS> {
readonly URI: F;
map<A, B>(fa: Kind<F, A>, f: (a: A) => B): Kind<F, B>;
}Arrayのインスタンス
インスタンスは、1つのURIに対するmapを実装します。Arrayのインスタンスでは、組み込みの配列メソッドを使ってマッピングします。Kind<"Array", A>はA[]に解決されます。
const arrayFunctor: Functor<"Array"> = {
URI: "Array",
map: (fa, f) => fa.map(f)
};Arrayのインスタンスを使う
Arrayのインスタンスでmapを呼び出すと、各要素を変換しながら配列というコンテナ型を維持できます。
const doubled = arrayFunctor.map([1, 2, 3], n => n * 2);
// doubled: number[] -> [2, 4, 6]Option型
値またはnullを表すOptionを定義して登録します。そのmapは値が存在する場合だけ関数を適用し、そうでなければnullをそのまま伝播させます。
interface URItoKind<A> { Option: A | null }
type Option<A> = A | null;Optionのインスタンス
OptionのインスタンスはOptionコンテナを維持します。値があればその値に対して関数を適用し、nullはnullのままにします。
const optionFunctor: Functor<"Option"> = {
URI: "Option",
map: (fa, f) => (fa === null ? null : f(fa))
};Optionのインスタンスを使う
これで、まったく異なるコンテナにも同じmapの形を使えるようになりました。存在する値は変換され、存在しない値はそのままになります。
const a = optionFunctor.map(5, n => n + 1); // 6
const b = optionFunctor.map(null, n => n + 1); // null1つの抽象化、多数のコンテナ
これが得られる成果です。arrayFunctorとoptionFunctorは同じFunctorインターフェースを共有します。Functor<F>を対象に書いたコードは、変更せずにどちらでも動作します。
function bumpAll<F extends URIS>(
F: Functor<F>,
fa: Kind<F, number>
): Kind<F, number> {
return F.map(fa, n => n + 1);
}汎用関数の呼び出し
bumpAllは、どのコンテナを受け取ったかを知る必要も気にする必要もありません。インスタンスとデータを渡すだけで、結果はコンテナ型を維持します。
const arr = bumpAll(arrayFunctor, [1, 2]); // number[] -> [2, 3]
const opt = bumpAll(optionFunctor, 9); // number | null -> 10Functorの法則
正しいFunctorは2つの法則に従います。恒等関数をマッピングしても何も変わらないこと、そして2つの関数を順番にマッピングした結果が、それらを合成してからマッピングした結果と等しいことです。上記のArrayとOptionのインスタンスは、どちらもこの法則を満たします。
// map(fa, x => x) === fa
// map(map(fa, f), g) === map(fa, x => g(f(x)))インスタンスを追加する
この抽象化は拡張可能なので、新しいコンテナはURIを登録してインスタンスを提供するだけで組み込めます。Tree、Result、Taskなども、同じ汎用コードでマッピングできるようになります。
// Register URI, implement Functor<"Tree">,
// and bumpAll works on trees too - no edits to bumpAll重要な理由
汎用Functorは、TypeScriptで関数型の抽象化を学ぶ入口です。Applicative、Monad、トラバーサルなどがその先にあります。それぞれがKind<F, A>に対するインターフェースであり、コンテナごとにインスタンスを持ちます。これで、fp-tsやEffectを可能にする仕組みを理解できました。
interface Monad<F extends URIS> extends Functor<F> {
of<A>(a: A): Kind<F, A>;
chain<A, B>(fa: Kind<F, A>, f: (a: A) => Kind<F, B>): Kind<F, B>;
}クイックチェック
汎用Functorについての理解度を確認しましょう。
まとめ
コンテナに対して汎用的に使えるmapを作成しました。
Functor<F>はKind<F, A>に対するmapを宣言します。- ArrayとOptionのインスタンスは、それぞれのURIに対してこれを実装します。
bumpAllのような汎用コードは、登録済みの任意のコンテナで動作します。- Functorの法則によってインスタンスの動作が適切に保たれ、より豊かな抽象化はこれを基盤に構築されます。
次のコース24では、型安全なパーサーを構築します。
よくある質問
「ジェネリックなFunctorとMapper」レッスンは無料ですか?
はい。「ジェネリックなFunctorとMapper」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、TypeScript Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 TypeScript Academyコースには全4レッスンが含まれています。
「ジェネリックなFunctorとMapper」で何を学びますか?
あらゆるコンテナに対応する汎用的なmap関数を記述します。 ブラウザで直接実行するハンズオンコードでTypeScript Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
TypeScript Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのTypeScript Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。
「ジェネリックなFunctorとMapper」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このTypeScript Academyレッスンでコードを書いて実行できますか?
はい。すべてのTypeScript Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- TypeScriptにおけるHKTの問題
- 型コンストラクターの定義
- 軽量HKTパターン
- ジェネリックなFunctorとMapper