高カインド型
higher kinded types (HKT)
博多?
高力(ちから)インド型ではない
プログラミングで最も基本にあるのは値である
1 "hello" true (1, true)
ある種類の値を集合のようにして抽象化して扱うのが型である
1 0 -3 などを含む Int 型
"hello" "こんにちは" "" などを含む String 型
関数 は値を受け取って値を返す
\n -> mod n 2 == 0 は Integer -> Bool 型。整数を受け取って真偽値を返す
多相 的な (generic な) 型というものがある Array<T> とか Vec<T> とか Maybe a [a] とか
型引数をとり、何らかの型を返す
ジェネリック型は型レベルでの関数とみることができる
type Pair a = (a, a) は型引数 a を受け取って (a, a) という型を返すので * -> * というカインドがつく
type Couple a b = (a, b) は * -> * -> * というカインドがつく
Haskellでは複数の型引数を取る場合、値の関数と同じように カリー化 される Maybe [] といった型は * -> * というカインド
Either (,) といった型は * -> * -> * というカインド
TypeScriptやRustではこのような「型を受け取って型を返す」という性質が表現できない
「引数として渡されたジェネリック型にIntを突っ込む」ような型を書けない
ApplyInt<Array> のようなことができない
Array<T> は Array<T> の形でしか扱えない
「引数として渡されたジェネリック型にIntを突っ込む」ような型が書ける
code:hs
type ApplyInt a = a Int -- aというジェネリック型を受け取り、引数としてIntを与えたものを返す
-- Maybeという*->*な型コンストラクタをApplyIntに渡すと
-- ApplyIntの中でIntを受け取るので 最終的な型はMaybe Int
type MaybeInt = ApplyInt Maybe
type ListInt = ApplyInt [] -- Int。リストの型コンストラクタ、ややキモい type SingleInt = ApplyInt (,) -- (Int)。さらにキモい
type DoubleInt = ApplyInt (,,) -- (Int, Int)。かわいい
-- 型コンストラクタの部分適用も可能
-- Eitherは* -> * -> *
type EitherFloatInt = IntOf (Either Float) -- Either Floatは* -> *
高カインド型を使った(値レベルの)関数を実装してみる
数値を受け取って、* -> * カインドに包んで返す関数を考える
普通に関数で書こうとすると制約の抽象度が高すぎて (とりうる場面があまりにも広すぎて) 実装できない
Maybe では Just、[] では [] のように実装で使うべき値コンストラクタはそれぞれで異なる
* -> * な型コンストラクタそれぞれに自力で実装を書く
code:hs
-- IntContainable :: (* -> *) -> Constraint
-- * -> *な型コンストラクタを取って制約を返す(?)これ自信ない
class IntContainable f where
-- fがMaybeならMaybe Intを返せばよい
-- カインド * -> * をとるIntContainableを実装した
-- 任意の型コンストラクタfについてInt値を受け取ってfにIntを適用した型の値を返す
-- packInt :: forall (f :: * -> *). IntContainable f => Int -> f Int
packInt n :: Int -> f Int
instance IntContainable [] where
instance IntContainable Maybe where
-- Int -> Maybe Int
packInt = Just
-- やはりここでも Either e として*->*を錬成している
-- EitherのFunctor,Applicative,Monadでも登場する
instance IntContainable (Either e) where
-- Int -> Either e Int
packInt = Right