高階型 (HKT)

型コンストラクタ(* -> * のような種(kind)を持つもの)を型パラメータに取れる多相。F[_] 自体を抽象でき、Functor / Monad / Traversable のような「コンテナによらない抽象」を書ける。

種(kind)— 「型の型」

例kind
Int, Bool*
List, Maybe* -> *
Either, Map* -> * -> *

HKT は「kind が * -> * の引数」を許す多相。普通のジェネリクスは *(具体型)しか抽象できない。

class Functor (f :: * -> *) where
  fmap :: (a -> b) -> f a -> f b   -- f は List でも Maybe でも IO でもよい

型コンストラクタの部分適用

HKT があると型コンストラクタを部分適用できる。Either e は Either :: * -> * -> * を1つ適用した * -> * で、Functor (Either e) のインスタンスにできる。Scala 3 は type lambda [X] =>> Either[E, X] で無名の部分適用を書く。この型レベルの部分適用が無いと、2引数以上のコンストラクタを Functor 等に載せられない。

なぜ多くの言語が非対応か

kind 体系が要り、型推論・実装(辞書渡し/単相化)が複雑化する。とくに「型コンストラクタを推論する」高階単一化は一般に決定不能で、注釈・制約が増える。

HKT が無い言語でのエミュレーション

手法言語/例仕組み
defunctionalizationOCaml “Lightweight HKP”(Yallop & White)コンストラクタを型タグ ('a, 'f) app に符号化し適用を関数化
URI/ブランド + レジストリTypeScript の fp-ts文字列リテラル型でコンストラクタを索引し Kind<URI, A> を引く
マクロ/コード生成各種インスタンスを機械生成して冗長さを隠す

いずれも「型コンストラクタを第一級にできない」言語で間接層を噛ませて Functor/Monad 抽象を近似する。冗長さ・エラーの読みにくさが代償。

高ランク多相(rank-N)との違い

混同しやすいが別物。HKT は「型コンストラクタ(* -> *)を型引数に取る」=種の高階性。高ランク多相は「forall を引数の内側に置く」= (forall a. a -> a) -> ... のような量化子の位置の話。直交する2つの型システム拡張。

なぜ重要か

F[_] を抽象できて初めて Monad / Comonad / tagless-final の F[_]: Monad のような「作用や文脈の型を差し替え可能にする」設計が書ける。HKT が無い言語では、これらの抽象が言語イディオムでしか近似できない。

関連

Graph