型と種類の違いは何ですか?


26

私はプログラミング言語のHaskellを学んでおり、a typeとaの違いが何であるかについて頭を包み込もうとしてkindいます。

私が理解しているように、a kind is a type of type。例えば、a ford is a type of carそしてa car is a kind of vehicle

これはこれについて考える良い方法ですか?

私の脳は、現在配線されている方法で、そのためford is a **type** of car、しかしまたcar is a **type** of vehicle一方で、同時にaのcar is a **kind** of vehicle。すなわち用語typekind交換可能です。

誰もこれに光を当てることができますか?


6
この議論につながったStack Overflowの投稿からちょうどここに来ました。私が詳細に答える資格があるかどうかはわかりませんが、あなたは間違いなく「タイプ」と「種類」という用語について文字通りすぎて、それらを英語の意味に関連付けようとしています(実際には同義語です) )。それらは技術用語として扱う必要があります。「タイプ」はすべてのプログラマーによく理解されていると思います。概念はすべての言語にとって重要であり、Javascriptのような弱いタイプの言語であっても、「Kind」はHaskellで「タイプのタイプ」を表す専門用語です。これで本当にすべてです。
ロビンジグモンド

2
@RobinZigmond:これらは技術用語であるという点では正しいですが、Haskellだけでなく広く使用されています。たぶん、この質問に起因するスタックオーバーフローの議論へのバックリンク?
アンドレイバウアー

@AndrejBauer Haskellの外部では使用されていないと言ったことはありません。確かに、「タイプ」は基本的にすべての言語で使用されています。私は実際にHaskellの外で「種類」に出会ったことはありませんが、Haskellは私が知っている唯一の関数型言語であり、この用語が他の場所で使用されていないということを言わないように注意しましたハスケル。(そして、あなたが要求するリンクはここにあります
ロビン・ジグモンド

MLファミリ言語にも種類があります。たとえば、標準MLおよびOCamlです。それらはその名前で明示的に公開されていないと思います。それらは署名として明示され、それらの要素は構造と呼ばれます
Andrej Bauer

1
より正確な英語の類推は、フォードは車の一種であり、車は車両の一種ですが、車の種類と車両の種類はどちらも同じ種類、つまり名詞です。一方、赤は車の色の種類であり、RPMは車の性能指標の一種であり、両方とも同じ種類の形容詞です。
スリーブマン

回答:


32

ここでは、「値」、「タイプ」、および「種類」には正式な意味があるため、一般的な英語の使用法や自動車の分類との類推は、これまでのところ得られません。

私の答えは、Haskellの文脈におけるこれらの用語の正式な意味に関するものです。これらの意味は、数学/ CS「型理論」で使用される意味に基づいています(実際には同じではありません)。したがって、これは「コンピューターサイエンス」の非常に良い答えにはなりませんが、Haskellのかなり良い答えになるはずです。

Haskell(および他の言語)では、式が持つことのできるのクラスを記述するプログラム式にを割り当てると役に立ちます。私はあなたがそれを表現していることを知ることは有用であろう理由を理解するのに十分な例を見てきましたことをここで前提とし、変数をして、常に型の値になるとはない、と言う、そしてそれぞれ。基本的に、を持つことは、広範囲の値で正しく動作する式/プログラムを書くのに役立ちますsqrt (a**2 + b**2)abDoubleStringBool

さて、あなたが気付いていないかもしれないのは、Haskell型、たとえば型シグネチャに現れるものです:

fmap :: Functor f => (a -> b) -> f a -> f b

実際には、それ自体はタイプレベルのHaskellサブ言語で書かれています。プログラムテキストFunctor f => (a -> b) -> f a -> f bは、文字通り、このサブ言語で記述された型式です。サブ言語は、(例えば、オペレータを含む->、この言語の右結合中置演算子である)、変数(例えば、fa、および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)

よさそうだ。だから、IntBool[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の型システムはすでに終了していないため、すでにパラドックスに満ちているので、大したことではないと考えられています。


「型と種類が実際に同じもの」である場合、型typetypeそれ自体であり、の必要はまったくありませんkind。それで、区別は正確に何ですか?
user21820

1
@ user21820、これに対処するかもしれないメモを最後に追加しました。簡単な答え:現代のGHC Haskellには実際に区別はありません。
KAブール

1
これは素晴らしい答えです-共有してくれてありがとう。それはよく書かれており、徐々に概念を紹介します-Haskellを数年書いていない人として、これは非常に高く評価されています!
ウルトラフェズ

@KABuhr:少し追加してくれてありがとう!
user21820

19

type:kind=set:class.

  • Bool タイプです
  • Type その要素は型であるため、種類です
  • Bool -> Int タイプです
  • Bool -> Type その要素は型を返す関数であるため、種類です
  • Bool * Int タイプです
  • Bool * Type その要素は型であり、1つのコンポーネントは型であるため

型理論では通常、すべての型の型を持つことは望ましくありません(pardoxesにつながります)。代わりに、他の小さなタイプを含むタイプであるユニバースを使用できます。たとえば、一連の宇宙U0U1U2U0BoolNatNatNatU1U0BoolU0U0U0Un+1UnUn×

U0U1U0U1U0**U_1


2
(GHC)Haskellには宇宙の概念はないと思います。Type :: Type公理です。その場合、「タイプ」と「種類」の区別は完全に人間の言語にあります。TrueにはタイプがありBool、にBoolはタイプがありType、それ自体にはタイプがありTypeます。型レベルのエンティティの型であることを強調するために、型を種類と呼ぶこともありますが、Haskellでは、まだ型にすぎません。Coqのようなユニバースが実際に存在するシステムでは「タイプ」は1つのユニバースを指し、「種類」は別のユニバースを指しますが、通常は無限に多くのユニバースが必要です。
HTNW

1
区別は単なる「人間の言語」ではなく、基本となる型システムの形式的な区別です。Type :: Type型と種類の両方を持ち、区別することは非常に可能です。また、Type :: TypeHaskell ではどのようなコードが実証されていますか?
アンドレイバウアー

1
また*、Haskellでは一種の宇宙であると言うべきです。彼らはそれをそれとは呼ばない。
Andrej Bauer

3
@AndrejBauer TypeからData.Kinds*あるべき同義語。当初は*プリミティブとしてしかありませんでしたが、現在ではGHC.Types.Type内部モジュールのように内部的に定義されGHC.Types、次にとして定義されていtype Type = TYPE LiftedRepます。私TYPEは本当のプリミティブであり、種類のファミリー(リフト型、非ボックス型、...)を提供すると思います。ここでの「不正な」複雑さの大部分は、実際の型理論上の理由ではなく、低レベルの最適化をサポートすることです。
カイ

1
まとめてみます。vが値の場合、タイプはになりますv :: TTがタイプの場合、タイプはになりますT :: K。タイプのタイプはその種類と呼ばれます。TYPE rep単語は一般的ではありませんが、次のように見える型はソートと呼ばれる場合があります。iff T :: TYPE repT、aのRHSに表示でき::ます。単語「種類」は、それにニュアンスがありますKT :: Kはないにようなものですが、v :: Kそれは同じだが、KK「種類が一種の場合は種類」、「種類はRHS上にある」と定義することもできます::が、それは使用法を正しくキャプチャしません。したがって、私の「人間の区別」の位置。
HTNW

5

値は、特定のは、あなたの私道に座っていることがそれに19206マイルで2011フォードマスタング赤のようなものです。

その特定の値は、非公式に、多くのタイプを持つことができます:マスタングであり、フォードであり、車であり、車両です。 「あなたのもの」、または「赤いもの」のタイプ、または...)。

(Haskellでは、一次近似(GADTがこのプロパティを破り、数値リテラルとOverloadedStrings拡張機能の魔法が少しあいまいになります)、値には、多くの非公式な「型」ではなく、1つの主な型があります。 stang。42は、この説明の目的でInt、Haskellには「数値」または「偶数」の型はありません-または、1つを作成することもできますが、からは切り離された型になりIntます。

現在、「マスタング」は「車」のサブタイプである可能性があります。マスタングであるすべての価値も車です。しかし、タイプ —または、Haskellの用語を使用する場合、「マスタング」の種類は「車」ではありません。「ムスタング」というタイプは、あなたが私道に駐車したり走り回ったりできるものではありません。「ムスタング」は、名詞、カテゴリ、または単なるタイプです。それらは非公式には「ムスタング」の一種です。

。(ここでも、Haskellはだけなので、各タイプレベルのもののための1つの種類を認識していないInt種類あり*、および他の種類。Maybe種類あり* -> *、および他の種類しかし、直感はまだ保持する必要があります。42Int、あなたが行うことができInt、それをY物事を。加減算などのIntない、それ自体Int、そのような数がようありませんInt + Int。あなたは非公式の人々はそれが言うのを聞くことIntNum、彼らはそこにあることを意味していることで、インスタンスNum型の型クラスInt-これは同じものではありませんそれが言うようにInt持っているようなもの NumIntHaskellで綴られている種類の「タイプ」を、持っています*。)

それで、すべての非公式な「タイプ」は単なる名詞またはカテゴリーではありませんか?すべてのタイプに同じ種類がありますか?退屈しているのに、なぜ種類について話すのでしょうか?

ここで、英語の類推が少し不安定になりますが、私は耐えます:英語の「所有者」という単語は、所有されているものの説明なしで、単独では意味をなさないふりをします。誰かがあなたを「所有者」と呼んだとしても、それはまったく意味をなさないだろうと思います。しかし、誰かがあなたを「車の所有者」と呼んだ場合、その意味を理解できます。

「所有者」には「車」と同じ種類はありませ。車について話すことはできますが、このメイクアップ版の英語では所有者について話すことはできません。「車の所有者」についてのみ話すことができます。「所有者」は、「車」などの「種類」がすでにあるものに適用された場合にのみ、種類が「名詞」のものを作成します。「所有者」の種類は「名詞->名詞」と言います。「所有者」は、名詞を受け取り、そこから別の名詞を生成する関数のようなものです。しかし、それ自体は名詞ではありません。

「車の所有者」は「車」のサブタイプではないことに注意してください!これは、車を受け入れたり返したりする関数ではありません!「車」とはまったく別のタイプです。ある時点で一定の金額があり、そのお金をディーラーに持って行った2本の腕と2本の脚で値を記述します。4つのホイールと1つのペイントジョブを持つ値については説明しません。また、「車の所有者」と「犬の所有者」は異なるタイプであり、一方でやりたいことは他方には適用できない場合があることに注意してください。

我々はそれが言うとき(同様に、Maybe種類がある* -> *Haskellで、我々はそれが正式に(無意味だという意味では、非公式に私たちは「持っていることについての話にすべての時間を)やるMaybe。」その代わり、私たちが持つことができるMaybe IntMaybe String、それらがのものであるため、親切*。)

したがって、種類について話すことのすべてのポイントは、「所有者」などの単語に関する推論を形式化し、「完全に構築された」無意味ではない型の値のみを取得することを強制できるようにすることです。


1
アナロジーが間違っていると言っているわけではありませんが、混乱を招く可能性があると思います。ダイクストラには、類推についていくつかの言葉があります。Google「計算科学を本当に教えることの残酷さについて」。
ラファエルカストロ

車のアナロジーがあり、車のアナロジーがあります。正式な型システムを説明する方法として、自然言語の暗黙の型構造を強調すること(確かに、後半に拡張しました)は、アナロジーを通して教えることと同じようなものだとは思いませんプログラムが何をしたいのか。
user11228628

1

私が理解しているように、種類は種類の一種です。

そうです-それが何を意味するのかを探りましょう。 IntまたはText具象型Maybe aですが、抽象型です。a特定の変数(または値/式/何でも)に必要な特定の値を決定するまで、具体的な型にはなりませんMaybe Text

私たちは、すなわちMaybe aあるタイプのコンストラクタは、単一の具体的なタイプ(例えばとる関数に似ているのでText)と(具体的なタイプを返しますMaybe Text。この場合には)。しかし、他の型コンストラクターは、具象型を返す前に、さらに多くの「入力パラメーター」を取得する場合があります。たとえば、具象型()を構築する前に、Map k v2つの具象型(eg IntおよびText)を取る必要がありますMap Int Text

したがって、Maybe aand List a型コンストラクターは、* -> *(Haskell関数シグネチャと同様に)として示す同じ「シグネチャ」を持ちます。1つの具象型を指定すると、具象型が吐き出されるからです。これをタイプの「種類」とMaybe呼びList、同じ種類を持っています。

具体型はkindを持っていると言われ*、Mapの例はkind * -> * -> *です。具体的な型を出力する前に2つの具体型を入力として受け取るためです。

ほとんどの場合、タイプコンストラクターに渡す「パラメーター」の数だけです、タイプコンストラクター内にネストされたタイプコンストラクターも取得できる* -> (* -> *) -> *ため、たとえば次のような種類になります。。

あなたがScala / Java開発者である場合、この説明も参考になるかもしれません:https : //www.atlassian.com/blog/archives/scala-types-of-a-higher-kind


これは正しくありません。Haskellでは、Maybe a、の同義語forall a. Maybe a、kindの多相型*、およびkind Maybeの単相型* -> *ます。
b0fh
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.