ここでは、「値」、「タイプ」、および「種類」には正式な意味があるため、一般的な英語の使用法や自動車の分類との類推は、これまでのところ得られません。
私の答えは、Haskellの文脈におけるこれらの用語の正式な意味に関するものです。これらの意味は、数学/ CS「型理論」で使用される意味に基づいています(実際には同じではありません)。したがって、これは「コンピューターサイエンス」の非常に良い答えにはなりませんが、Haskellのかなり良い答えになるはずです。
Haskell(および他の言語)では、式が持つことのできる値のクラスを記述するプログラム式に型を割り当てると役に立ちます。私はあなたがそれを表現していることを知ることは有用であろう理由を理解するのに十分な例を見てきましたことをここで前提とし、変数をして、常に型の値になるとはない、と言う、そしてそれぞれ。基本的に、型を持つことは、広範囲の値で正しく動作する式/プログラムを書くのに役立ちます。sqrt (a**2 + b**2)
a
b
Double
String
Bool
さて、あなたが気付いていないかもしれないのは、Haskell型、たとえば型シグネチャに現れるものです:
fmap :: Functor f => (a -> b) -> f a -> f b
実際には、それ自体はタイプレベルのHaskellサブ言語で書かれています。プログラムテキストFunctor f => (a -> b) -> f a -> f b
は、文字通り、このサブ言語で記述された型式です。サブ言語は、(例えば、オペレータを含む->
、この言語の右結合中置演算子である)、変数(例えば、f
、a
、およびb
)別の種類の発現、および「アプリケーション」(例えば、f a
されf
に適用されますa
)。
式のクラスを記述するためにプログラム式に型を割り当てることは、多くの言語でどのように役立つかについて言及しましたか?さて、このタイプレベル部分言語では、式はに評価タイプ(というよりも値)と、それは、割り当てに役立つされて終わるの種類のクラス記述するために式を入力するタイプの彼らが表現するために許可されています。基本的に、種類を持つことは、広範囲の型で正しく機能する型式を書くのに役立ちます。
だから、値がにあるタイプとタイプがにある種類、およびタイプは、私たちが書く助け値ながら、レベルのプログラムを種類は、私たちが書く助けタイプレベルのプログラムを。
これらの種類はどのように見えますか?さて、型の署名を考えてみましょう:
id :: a -> a
型式が場合はa -> a
有効であるとされ、どのような種類のタイプは、我々は変数を許可すべきa
であることを?さて、型式:
Int -> Int
Bool -> Bool
有効に見えるため、タイプ Int
とタイプBool
は明らかに正しい種類です。しかし、次のようなさらに複雑なタイプ:
[Double] -> [Double]
Maybe [(Double,Int)] -> Maybe [(Double,Int)]
有効に見えます。実際、id
関数を呼び出すことができるはずなので、
(a -> a) -> (a -> a)
よさそうだ。だから、Int
、Bool
、[Double]
、Maybe [(Double,Int)]
、およびa -> a
などのすべての表情タイプ権利の一種。
言い換えれば、種類は1つだけのように見えるので*
、Unixワイルドカードのように呼び出しましょう。すべてのタイプには同じ種類があり *
、話の終わりです。
右?
まあ、そうではありません。はMaybe
、すべて単独で、型式と同じくらい有効であることがわかりますMaybe Int
(ほぼ同じようにsqrt
、すべて単独で、値式と同じように有効ですsqrt 25
)。 ただし、次の型式は無効です。
Maybe -> Maybe
そのため、一方、Maybe
型の式である、それは表していない種類のタイプの値を持つことができます。だから、それは私たちが定義すべき方法です*
-それは値を持つ種類の種類です。Double
またはのような「完全な」タイプMaybe [(Double,Int)]
が含まれますが、などの不完全で価値のないタイプは除外されEither String
ます。簡単にするために、これらの完全な種類の種類を*
「コンクリート型」と呼びますが、この用語は普遍的ではなく、「コンクリート型」は、たとえばC ++プログラマとは非常に異なるものを意味する場合があります。
さて、型の式ではa -> a
、限り、タイプとしてa
持っているようなもの *
(具体的なタイプの一種)、型の式の結果はa -> a
しますも持っているようなもの *
(すなわち、具体的なタイプの一種)。
だから、何種類のタイプがありますかMaybe
?まあ、Maybe
具体的なタイプに適用して、別の具体的なタイプを生成できます。だから、Maybe
取るタイプレベルの機能のような少しのように見えるタイプの種類を *
と返すタイプのようなものを *
。私たちが取った値レベルの機能があった場合値の種類を Int
、返された値の種類を Int
、我々はそれを与えるだろうタイプの署名をInt -> Int
、その類推によって、私たちは与えるべき種類の署名を。GHCiは同意します:Maybe
* -> *
> :kind Maybe
Maybe :: * -> *
に戻る:
fmap :: Functor f => (a -> b) -> f a -> f b
この型シグネチャでは、変数にf
はkind * -> *
と変数がa
ありb
、kindがあり*
ます。組み込み演算子に->
はkindがあります* -> * -> *
(*
左側にkindのタイプを取り、右側にkind を取り、kindのタイプも返します*
)。この親切な推論規則から、あなたはそれを推測できるa -> b
ようなもので、有効なタイプであり*
、f a
そしてf b
また、種類によって有効なタイプで*
、かつ(a -> b) -> f a -> f b
種類の有効なタイプです*
。
言い換えると、コンパイラは、型式を「種類チェック」して(a -> b) -> f a -> f b
、正しい型の変数に対して有効であることを確認するのと同じ方法sqrt (a**2 + b**2)
で、正しい種類の型変数に対して有効であることを確認できます。
「タイプ」と「種類」に別々の用語を使用する(つまり、「タイプのタイプ」について話さない)理由は、ほとんど混乱を避けるためです。上記の種類は、型とは非常に異なって見え、少なくとも最初はまったく異なる動作をするようです。(たとえば、すべての「通常の」型は同じ種類で*
あり、種類a -> b
は*
そうではないという考えに頭を包むには時間がかかります* -> *
。)
これの一部は歴史的なものでもあります。GHC Haskellが進化するにつれて、値、型、および種類の区別があいまいになり始めました。最近では、値を型に「昇格」させることができ、型と種類は実際には同じものです。そのため、現代のHaskellでは、値には型とARE型(ほぼ)の両方があり、型の種類はより多くの型です。
@ user21820は、「タイプと種類は実際には同じものです」という追加説明を求めました。少し明確にするために、最新のGHC Haskell(バージョン8.0.1以降)では、型と種類はほとんどのコンパイラコードで一様に扱われます。コンパイラは、値の型について不平を言っているのか、型の型について不平を言っているのかによって、「型」と「種類」を区別するためにエラーメッセージを作成します。
また、拡張機能が有効になっていない場合、サーフェス言語で簡単に区別できます。たとえば、(値の)型は構文(型シグネチャなど)で表現されますが、(型の)種類は完全に暗黙的であり、明示的な構文はありません。
ただし、適切な拡張機能を有効にすると、型と種類の区別はほとんどなくなります。例えば:
{-# LANGUAGE GADTs, TypeInType #-}
data Foo where
Bar :: Bool -> * -> Foo
ここでBar
は、(値と)両方のタイプです。タイプとして、その種類があるBool -> * -> Foo
種類の型取り型レベルの関数である、Bool
(タイプであるが、一種)や種類のタイプ*
および種類のタイプを生成しますFoo
。そう:
type MyBar = Bar True Int
正しく種類をチェックします。
@AndrejBauerが彼の答えで説明しているように、型と種類を区別するこの失敗は安全ではありません- *
型/種類がそれ自体である(現在のHaskellの場合)タイプ/種類を持つことは逆説につながります。しかし、Haskellの型システムはすでに終了していないため、すでにパラドックスに満ちているので、大したことではないと考えられています。