軽量HKTパターン
Kind/URIのデファンクショナライゼーション技法を実装します。
「軽量HKTパターン」はCoddyKit上の無料TypeScript Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはTypeScript Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 TypeScript Academyコースには全4レッスンが含まれています。
デファンクショナライゼーション
このエンコーディングの背後にある仕組みには、デファンクショナライゼーションという名前があります。実際の高階型関数を使う代わりに、各関数をタグで表現し、単一の第一階の検索で解決します。URItoKindレジストリがその検索テーブルです。
type Kind<F extends URIS, A> = URItoKind<A>[F];
// One lookup replaces true type-function applicationKind型
Kind<F, A>は、このパターンを外部に見せるための型です。「AにFコンストラクターを適用した型」と読みます。レジストリのインデックス参照を、わかりやすい名前の背後に隠します。
type A = Kind<"Array", number>; // number[]
type B = Kind<"Option", string>; // string | nullFをURISに制約する
Fは登録済みのURIでなければなりません。extends URISで制約することで、有効なタグだけを使用でき、タイプミスをコンパイルエラーにできます。
type Kind<F extends URIS, A> = URItoKind<A>[F];
// Kind<"Arrey", number> -> error, not a valid URI汎用シグネチャの記述
これで、以前は記述できなかったFunctorのシグネチャを書けるようになります。すべてのF<A>をKind<F, A>に置き換えると、コンパイラーは受け入れてくれます。
interface Functor<F extends URIS> {
map<A, B>(fa: Kind<F, A>, f: (a: A) => B): Kind<F, B>;
}解決の実例
Fを具体的なURIに固定すると、Kindは実際のコンテナ型に解決されます。Functor<"Array">には、A[]からB[]へのmapがあります。
type ArrayFunctor = Functor<"Array">;
// map: <A, B>(fa: A[], f: (a: A) => B) => B[]複数の型引数
Either<E, A>のように、2つの引数を取るコンストラクターもあります。fp-tsでは、URItoKind2とKind2<F, E, A>を使って、カインド* -> * -> *に対応するようパターンを拡張しています。レジストリを増やすことで、この考え方を拡張できます。
interface URItoKind2<E, A> {
Either: { left: E } | { right: A };
}
type Kind2<F extends URIS2, E, A> = URItoKind2<E, A>[F];fp-tsの規約
fp-tsは、このコース全体で採用する規約を確立しました。データ型ごとに1つのURI文字列を用意し、宣言マージで拡張するURItoKindレジストリと、解決に使うKindを用います。この規約がわかると、fp-tsやEffectの型シグネチャを読めるようになります。
// In fp-ts you will see exactly:
// declare module "fp-ts/HKT" { interface URItoKind<A> { ... } }インスタンスレコード
抽象化の「インスタンス」とは、特定のURIに対してそれを実装する値です。インスタンスは具体的なmapを保持します。次のレッスンでArrayのインスタンスを記述します。ここではその形だけを確認します。
declare const arrayFunctor: Functor<"Array">;
const out = arrayFunctor.map([1, 2, 3], n => n * 2); // number[]「軽量」と呼ばれる理由
軽量HKTパターンと呼ばれるのは、コンパイラーの変更も特別な構文も必要なく、インターフェースとインデックスアクセスだけで実現できるからです。URIやレジストリを用意する手間は少しかかりますが、コンテナに対する真の汎用コードが得られます。
type Kind<F extends URIS, A> = URItoKind<A>[F];
// No magic, just a typed lookup table制限事項
このパターンは冗長で、ドメインではなくエンコーディングに関する情報がエラーメッセージに現れるため、内容がわかりにくいことがあります。また、すべてのコンテナを登録する必要もあります。多くのアプリでは、自分で構築するのではなく、すでにこの仕組みを採用しているライブラリを利用します。
// Trade-off: real abstraction power vs. encoding ceremony全体を組み立てる
軽量HKTの完全な手順は、URIを定義し、URItoKindに登録し、Kind<F, A>を使って抽象化を定義し、URIごとにインスタンスを提供することです。これが、TypeScriptの汎用関数型ライブラリを支えるツールキットの全体です。
type Kind<F extends URIS, A> = URItoKind<A>[F];
interface Functor<F extends URIS> {
map<A, B>(fa: Kind<F, A>, f: (a: A) => B): Kind<F, B>;
}クイックチェック
軽量HKTパターンについての理解度を確認しましょう。
まとめ
軽量HKTパターンを組み立てました。
- デファンクショナライゼーションによって、型関数の適用を検索に置き換えます。
Kind<F, A>はURIと引数から具体的な型を解決します。- 汎用シグネチャでは
F<A>の代わりにKindを使います。 Kind2を使えば2つの引数にも対応でき、これがfp-tsの規約です。
次は、実際のインスタンスを使った汎用mapです。
よくある質問
「軽量HKTパターン」レッスンは無料ですか?
はい。「軽量HKTパターン」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、TypeScript Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 TypeScript Academyコースには全4レッスンが含まれています。
「軽量HKTパターン」で何を学びますか?
Kind/URIのデファンクショナライゼーション技法を実装します。 ブラウザで直接実行するハンズオンコードでTypeScript Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
TypeScript Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのTypeScript Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「軽量HKTパターン」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このTypeScript Academyレッスンでコードを書いて実行できますか?
はい。すべてのTypeScript Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。