Kind
kindは、Intだと*
、Maybeだと* -> *
などと表し、型が型をとる場合がわかるラベルみたいなもの。(*
以外にもいくつか種類がある。)
kindは:k
や:kind
で確認できる。
>:k Int
Int :: *
>:k Maybe
Maybe :: * -> *
>:k Map
Map :: * -> * -> *
例えばこのように定義した時、
Prelude> data Foo a b
Prelude> :k Foo
Foo :: * -> * -> *
aとbを受け取るので* -> * -> *
だが、
Prelude> data Foo a b = Foo (a b)
Prelude> :k Foo
Foo :: (* -> *) -> * -> *
Prelude> :k Foo Maybe Int
Foo Maybe Int :: *
こうすると、Fooのaはbを受け取るのだから、aが* -> *
だと推論してくれる。
なのでFooのaは* -> *
な型しか受け取れない。
これをわかりやすくkind注釈を書くこともできる。
Prelude> :set -XKindSignatures
Prelude> data Foo (a :: * -> *) (b :: *) = Foo (a b)
余談だが、この辺Scalaだとkindを意識して型定義する必要があるのでわかりやすい。
scala> case class Foo[F[_], A](f: F[_], a: A)
defined class Foo
scala> :k -v Foo
Foo's kind is X[F[A1],A2]
(* -> *) -> * -> *
*
はIntみたいにkindを取らない型しか表せないから単相だが、多相なkindを表すこともできる。
Prelude> :set -XPolyKinds
Prelude> data Foo (a :: k) = Foo
Prelude> :k Foo
Foo :: k -> *
Prelude> :k Foo Int
Foo Int :: *
Prelude> :k Foo Maybe
Foo Maybe :: *
型の昇格
DataKinds拡張を使うことで、値コンストラクタを型に使うことができる。
Prelude> :set -XDataKinds
Prelude> :set -XKindSignatures
Prelude> data Color = Red | Blue
Red、Blueは値コンストラクタだが、
Prelude> data MkColor (a :: Color)
Prelude> :k MkColor Red
MkColor Red :: *
のように型として使うことができるる。さらに型コンストラクタは種上にも定義することができるなど昇格して便利に使うことができる。
もっとちゃんと書こうと思ったけど時間ないのでこの辺で...