Haskellでは、(値レベルの)式はtypesに分類され、次のように::
で表記できます。3 :: Int
、"Hello" :: String
、(+ 1) :: Num a => a -> a
。同様に、タイプは種類に分類されます。 GHCiでは、コマンド:kind
または:k
を使用して、型式の種類を検査できます。
> :k Int
Int :: *
> :k Maybe
Maybe :: * -> *
> :k Either
Either :: * -> * -> *
> :k Num
Num :: * -> Constraint
> :k Monad
Monad :: (* -> *) -> Constraint
*
は「具象型」または「値」または「実行時値」の一種であるという定義が浮かんでいます。たとえば、 Learn You A Haskell を参照してください。それはどれほど真実ですか? いくつか質問種類について 通過するトピックに対処するものがありますが、標準的で正確な説明があればいいのですが*
の。
*
は正確にとはどういう意味ですか?そして、それは他のより複雑な種類とどのように関連していますか?
また、DataKinds
またはPolyKinds
拡張子は答えを変更しますか?
種類言語の最も基本的な形式では、種類*
と種類コンストラクター->
のみが存在する場合、*
は、値とのタイプの関係に立つことができる種類のものです。異なる種類のものは、値のタイプになることはできません。
値を分類するためのタイプが存在します。同じ型を持つすべての値は、型チェックの目的で交換可能であるため、型チェッカーは特定の値ではなく、型のみを考慮する必要があります。つまり、すべての実際の値が存在する「値レベル」と、それらのタイプが存在する「タイプレベル」があります。 「type-of」関係は、2つのレベル間のリンクを形成し、単一のタイプが(通常)多くの値のタイプになります。 Haskellはこれら2つのレベルを非常に明確にしています。型レベルのものFoo
(種類data Foo = Foo Int Chat Bool
の型)と値レベルのものFoo
(型*
のコンストラクター)を宣言したInt -> Char -> Bool -> Foo
のような宣言を持つことができるのはそのためです。関連する2つのFoo
は、単に異なるレベルの異なるエンティティを参照します。Haskellはこれらを完全に分離しているため、参照しているレベルを常に把握できるため、異なるレベルの物に同じ名前を付けることができます(混乱を招く場合があります)。
しかし、それ自体が構造を持つ型(Maybe Int
、型Maybe
に適用される型コンストラクターInt
など)を導入するとすぐに、型レベルで存在するものがあり、実際には任意の値。タイプがMaybe
だけの値はなく、タイプMaybe Int
の値のみ(およびMaybe Bool
、Maybe ()
、さらにはMaybe Void
など)です。したがって、値を分類する必要があるのと同じ理由で、タイプレベルのものを分類する必要があります。特定の型式のみが実際に値の型になり得るものを表しますが、それらの多くは「種類チェック」の目的で交換可能に機能します(正しい型であるかどうかタイプであると宣言されている値レベルのものは、別のレベルの問題です)。1
したがって、*
(「タイプ」と発音されることが多い)が基本的な種類です。それは、値のタイプであると言うことができるすべてのタイプレベルのものの種類です。 Int
には値があります。したがって、そのタイプは*
です。 Maybe
には値がありませんが、引数を取り、値を持つ型を生成します。これにより、___ -> *
のようなものが得られます。 Maybe
の引数がJust a
に表示される値の型として使用されていることを確認することで、空白を埋めることができます。したがって、その引数も値の型(種類*
)である必要があり、Maybe
の種類は* -> *
である必要があります。等々。
星と矢印のみを含む種類を扱っている場合、種類*
の型式のみが値の型です。他の種類(例:* -> (* -> * -> *) -> (* -> *)
)には、値を含む実際の型ではない他の「型レベルのエンティティ」のみが含まれます。
PolyKinds
は、私が理解しているように、この図をまったく変更しません。種類レベルで多形宣言を行うことができます。つまり、(星と矢印に加えて)種類の言語に変数が追加されます。これで、タイプレベルの種類のk -> *
を考えることができます。これは、種類* -> *
または(* -> *) -> *
または(* -> (* -> *)) -> *
のいずれかとして機能するようにインスタンス化できます。タイプレベルで(a -> b) -> [a] -> [b]
を取得したのとまったく同じ種類のパワーを取得しました。可能なすべてのマップ関数を個別に作成する代わりに、変数を含む型で1つのmap
関数を作成できます。しかし、値のタイプであるタイプレベルのものを含む種類はまだ1つだけです:*
。
DataKinds
はまた、親切な言語に新しいことを導入します。ただし、事実上、新しい型レベルのエンティティを含む任意の新しい種類を宣言できるようにします(通常のdata
宣言で、新しい値レベルのエンティティを含む任意の新しい型を宣言できるのと同じです)。しかし、3つのレベルすべてにわたるエンティティの対応で物事を宣言することはできません。 data Nat :: Z | S Nat
があり、DataKinds
を使用して種類レベルに上げる場合、タイプレベルに存在するNat
という名前の2つの異なるものがあります(値レベルのタイプとしてZ
、S Z
、S (S Z)
など。 )、および種類レベルで(type-levelZ
、S Z
、S (S Z)
の種類として)。 type-levelZ
は、値のタイプではありません。 valueZ
は、タイプレベルのNat
ではなく、type-levelZ
(これは、種類*
)に存在します。したがって、DataKinds
は、新しいユーザー定義のものを種類言語に追加します。これは、型レベルでの新しいユーザー定義のものの種類ですが、値の型になり得る唯一の型レベルのものは種類である場合があります。 *
。
私が知っている親切な言語に加えて、これが本当に変わるのは、@ ChristianConkleの回答で言及されている#
などの種類だけです(もう2つあると思いますか?私は「 ByteArray#
などの低レベル」タイプ)。これらは、ポリモーフィック関数が含まれている場合でも、GHCが異なる方法で処理するために知っておく必要のある値を持つタイプです(ボックス化して遅延評価できると想定しないなど)。したがって、必要な知識を添付するだけでは不十分です。これらの値の型とは異なる方法で処理されるか、値のポリモーフィック関数を呼び出すときに失われます。
1 したがって、「タイプ」という言葉は少し混乱する可能性があります。 実際に値レベルのものとタイプの関係にあるものを指すために使用されることがあります(これは、「Maybe
はタイプではない」と人々が言うときに使用される解釈です、それは型コンストラクタです」)。また、タイプレベルで存在するものを参照するために使用されることもあります(この解釈では、Maybe
は実際にはタイプです)。この投稿では、「type」を省略形として使用するのではなく、「type-levelthings」を非常に明示的に参照しようとしています。
種類について学ぼうとしている初心者(タイプのタイプと考えることができます)には、 Haskellを学ぶ 本のこの章をお勧めします。
私は個人的にこのように種類を考えます:
具体的なタイプがあります。 Int
、Bool
、String
、_[Int]
_、_Maybe Int
_または_Either Int String
_。
これらはすべて_*
_の種類があります。どうして?これ以上の型をパラメーターとして受け取ることができないためです。 Int
は、Int
です。 _Maybe Int
_は_Maybe Int
_です。しかし、Maybe
または_[]
_またはEither
はどうですか?
Maybe
と言うときは、パラメーターを指定しなかったため、具体的な型はありません。 _Maybe Int
_または_Maybe String
_は異なりますが、どちらも_*
_の種類がありますが、Maybe
は種類の種類_*
_が種類を返すのを待っています_*
_。明確にするために、GHCIの_:kind
_コマンドが私たちに何を伝えることができるかを見てみましょう:
_Prelude> :kind Maybe Int
Maybe Int :: *
Prelude> :kind Maybe
Maybe :: * -> *
_
リストでも同じです。
_Prelude> :k [String]
[String] :: *
Prelude> :k []
[] :: * -> *
_
Either
はどうですか?
_Prelude> :k Either Int String
Either Int String :: *
Prelude> :k Either Int
Either Int :: * -> *
_
Either
はパラメーターを受け取る関数と直感的に考えることができますが、パラメーターはtypesです。
_Prelude> :k Either Int
Either Int :: * -> *
_
_Either Int
_が型パラメーターを待機していることを意味します。