- TypeScript で型パラメータに型パラメータ取りたいっていたのは Higher-kinded types だった
- TSでは実現できないので, fp-ts とかではエンコードしている
HKTのencode
interface Functor {
map: <A, B>(f: (a: A) => B, fa: ?) => ?
}Option<T> に対してinstanceを定義したい
interface HKT<F, A> {
_URI: F
_A: A
}F: コンテナ型の識別子
A: 中身の型
ex. HKT<"Option", string> = Option<string>
interface Functor {
map: <A, B>(f: (a: A) => B, fa: HKT<F, A>) => HKT<F, A>
}これを使えるように書き換える
const URI = 'Option'
export class None {
readonly _URI!: URI;
readonly _A!: never
readonly tags: "None" = "None";
}
export class Some<A> {
readonly _URI!: URI;
readonly _A!: A
readonly tags: "Some" = "Some";
constructor(readonly value: A) {}
}
export type Option<A> = None | Some<A>;
const map = <A, B>(f: (a: A) => B, fa: Option<A>): Option<B> => {
switch (fa.tag) {
case "None":
return fa;
case "Some":
return some(f(fa.value));
}
};
const option: Functor<URI> = {
// error
map,
};HKT<"Option", A> != Option<A> なのでエラーが出る.
const map = <A, B>(f: (a: A) => B, hfa: HKT<URI, A>): Option<B> {};型を合わせるが, HKT<"Option", A> と推論されてしまう.
実際の型に変換する型レベルの辞書を用意して拡張していく. TSは interface 複数宣言するとmergeされるので.
const a: Type<"Option", number> = some(1); // Option<number>
interface Functor<F extends URIS> {
map: <A, B>(f: (a: A) => B, fa: Type<F, A>) => Type<F, B>
}