Hier haben "Werte", "Typen" und "Arten" formale Bedeutungen. Wenn Sie also die gebräuchliche englische Sprache oder Analogien zur Klassifizierung von Automobilen berücksichtigen, werden Sie nur so weit kommen.
Meine Antwort bezieht sich auf die formale Bedeutung dieser Begriffe im Zusammenhang mit Haskell; Diese Bedeutungen basieren auf den Bedeutungen, die in der mathematischen / CS "Typentheorie" verwendet werden (obwohl sie nicht wirklich identisch sind). Das wird also keine sehr gute "Computerwissenschaft" -Antwort sein, aber es sollte eine ziemlich gute Haskell-Antwort sein.
In Haskell (und anderen Sprachen), endet es auf ein Wesen hilfreich zuweisen Typen zu einem Programmausdruck, der die Klasse von beschreiben Werten der Ausdruck erlaubt ist zu haben. Ich gehe davon aus, dass Sie genug Beispiele gesehen haben, um zu verstehen, warum es nützlich wäre, das im Ausdruck zu wissensqrt (a**2 + b**2)
, die Variablen a
und b
werden immer Werte vom Typ sein Double
und nicht, sagen, String
und Bool
jeweils. Grundsätzlich hilft uns das Haben von Typen beim Schreiben von Ausdrücken / Programmen, die über einen weiten Bereich von Werten korrekt funktionieren .
Vielleicht haben Sie nicht bemerkt, dass Haskell-Typen wie die in Typensignaturen vorkommenden:
fmap :: Functor f => (a -> b) -> f a -> f b
sind eigentlich selbst in einer Typ-Level-Haskell-Subsprache geschrieben. Der Programmtext Functor f => (a -> b) -> f a -> f b
ist - im wahrsten Sinne des Wortes - ein typischer Ausdruck in diesem Subsprache geschrieben. Die Untersprache enthält Operatoren (beispielsweise ->
ist eine rechte assoziative Infixoperator in dieser Sprache), Variablen ( zum Beispiel f
, a
, und b
), und „Anwendung“ eines Typ Ausdruck zu einem anderen ( zum Beispiel f a
wird f
angewandt a
).
Habe ich erwähnt, wie hilfreich es in vielen Sprachen ist, Programmausdrücken Typen zuzuweisen, um Klassen von Ausdruckswerten zu beschreiben? Nun, in dieser Subsprache auf Typebene werden Ausdrücke als Typen (und nicht als Werte ) ausgewertet, und es ist letztendlich hilfreich, sie zuzuweisen Arten , um die Klassen von Typen zu beschreiben, die sie darstellen dürfen. Grundsätzlich hilft uns das Haben von Arten beim Schreiben von Typausdrücken, die über einen weiten Bereich von Typen korrekt funktionieren .
So Werte sind Typen als Typen zu sind Arten und Typen helfen uns schreiben Wert -Niveau Programme während Arten helfen uns schreiben Art -Niveau Programme.
Wie sehen diese Arten aus? Nun, betrachten Sie die Typensignatur:
id :: a -> a
Wenn der Typausdruck a -> a
gültig sein soll, welche Arten von Typen sollten wir die Variablen zulassen a
? Nun, die Typausdrücke:
Int -> Int
Bool -> Bool
siehst gültig aus, also die typen Int
und Bool
sind offensichtlich von der richtigen art . Aber auch kompliziertere Typen wie:
[Double] -> [Double]
Maybe [(Double,Int)] -> Maybe [(Double,Int)]
siehst gültig aus. In der Tat, da wir in der Lage sein sollten, id
Funktionen aufzurufen , sogar:
(a -> a) -> (a -> a)
sieht gut aus. Also Int
, Bool
, [Double]
, Maybe [(Double,Int)]
, und a -> a
alle sehen aus wie Typen der richtigen Art .
Mit anderen Worten, es scheint nur eine Art zu geben. Nennen wir es *
wie einen Unix-Platzhalter und jeden Typ hat die gleiche Art *
, Ende der Geschichte.
Recht?
Nicht ganz. Es stellt sich heraus, dass Maybe
ein Typ-Ausdruck für sich genommen genauso gültig ist wie Maybe Int
(in etwa der gleiche Weg sqrt
, für sich genommen, ist ein Wert-Ausdruck genauso gültig wie sqrt 25
). jedoch folgende Typausdruck ist jedoch ungültig:
Maybe -> Maybe
Denn während Maybe
ein Ausdruck des Typs ist, ist es nicht die repräsentiert Art von Typ , die Werte aufweisen können. Also, das ist , wie wir definieren sollte *
- es ist die Art von Typen , die Werte aufweisen; es enthält "vollständige" Typen wie Double
oderMaybe [(Double,Int)]
schließt jedoch unvollständige, wertlose Typen wie aus Either String
. Der Einfachheit halber werde ich diese vollständigen Arten von Arten *
"konkrete Arten" nennen, obwohl diese Terminologie nicht universell ist, und "konkrete Arten" können etwas ganz anderes bedeuten als beispielsweise ein C ++ - Programmierer.
Nun, in der Art Ausdruck a -> a
, solange Art a
hat Art *
(die Art von Beton - Typen), das Ergebnis der Art Ausdrucks a -> a
wird auch haben Art *
(dh die Art von Beton - Typen).
Also, welche Art von Typ ist Maybe
? Nun, Maybe
kann auf einen Betontyp angewendet werden, um einen anderen Betontyp zu ergeben. Also, Maybe
sieht aus wie ein wenig wie eine Typ-Level - Funktion , die eine nimmt Art von Art *
und gibt eine Art von Art *
. Wenn wir einen Wert Level - Funktion haben , die einen nahm Wert von Typ Int
und einen zurück Wert von Typ Int
, würden wir ihm eine geben Art Unterschrift Int -> Int
, so analog sollten wir geben Maybe
eine Art Unterschrift * -> *
. GHCi stimmt zu:
> :kind Maybe
Maybe :: * -> *
Zurück gehen zu:
fmap :: Functor f => (a -> b) -> f a -> f b
In dieser Typensignatur hat Variable f
Art * -> *
und Variablen a
und b
Art *
; Der eingebaute Operator ->
hat Art * -> * -> *
(er nimmt eine Art *
auf der linken und eine auf der rechten Seite und gibt auch eine Art zurück *
). Daraus und aus den Rückschlüssen auf die Art können Sie ableiten, dass a -> b
es sich um einen gültigen Typ mit Art *
handelt f a
und dass f b
es sich auch um gültige Typen mit Art *
handelt und dass es sich um einen gültigen Typ (a -> b) -> f a -> f b
handelt *
.
Mit anderen Worten, der Compiler kann den Typausdruck " (a -> b) -> f a -> f b
überprüfen" sqrt (a**2 + b**2)
, um zu überprüfen, ob er für Typvariablen des richtigen Typs gültig ist, ebenso wie er "überprüft" , ob er für Variablen des richtigen Typs gültig ist.
Der Grund für die Verwendung separater Ausdrücke für "Typen" gegenüber "Arten" (dh nicht über die "Typen von Typen" zu sprechen) ist meist nur, um Verwirrung zu vermeiden. Die Arten oben Blick sehr verschieden von Typen und, zumindest auf dem ersten, scheinen ganz anders zu verhalten. (Zum Beispiel dauert es einige Zeit , den Kopf wickeln sich um die Idee , dass jeder „normalen“ hat die gleiche Art *
und die Art a -> b
ist *
nicht * -> *
.)
Einiges davon ist auch historisch. Mit der Entwicklung von GHC Haskell verschwimmen die Unterschiede zwischen Werten, Typen und Arten. In diesen Tagen können Werte in Typen "befördert" werden, und Typen und Arten sind wirklich dasselbe. Daher haben in modernen Haskell-Werten sowohl Typen als auch ARE- Typen (fast), und die Typenarten sind nur mehr Typen.
@ user21820 fragte nach einer zusätzlichen Erklärung für "Typen und Arten sind wirklich dasselbe". Um es ein bisschen klarer zu machen: In modernen GHC Haskell (seit Version 8.0.1, glaube ich) werden Typen und Arten im Compiler-Code weitgehend einheitlich behandelt . Der Compiler unternimmt einige Anstrengungen bei Fehlermeldungen, um zwischen "Typen" und "Arten" zu unterscheiden, je nachdem, ob er sich über den Typ eines Werts oder den Typ eines Typs beschwert.
Auch wenn keine Erweiterungen aktiviert sind, sind sie leicht zu unterscheiden in der Oberflächensprache. Zum Beispiel haben Typen (von Werten) eine Darstellung in der Syntax (z. B. in Typensignaturen), aber Arten (von Typen) sind - glaube ich - völlig implizit und es gibt keine explizite Syntax, in der sie erscheinen.
Wenn Sie jedoch die entsprechenden Erweiterungen aktivieren, verschwindet die Unterscheidung zwischen Typen und Arten weitgehend. Beispielsweise:
{-# LANGUAGE GADTs, TypeInType #-}
data Foo where
Bar :: Bool -> * -> Foo
Hier Bar
ist (sowohl ein Wert als auch) ein Typ. Als Typ ist seine Art Bool -> * -> Foo
eine Funktion auf Typebene, die einen Typ Bool
(der ein Typ, aber auch eine Art ist) und einen Typ einer Art annimmt und einen Typ einer Art *
erzeugt Foo
. So:
type MyBar = Bar True Int
korrekt überprüft.
Wie @AndrejBauer in seiner Antwort erklärt, ist diese Unterscheidung zwischen Typen und Arten unsicher - ein Typ / eine Art, *
deren Typ / Art selbst ist (was im modernen Haskell der Fall ist), führt zu Paradoxen. Das Typensystem von Haskell ist jedoch aufgrund der Nichtbeendigung bereits voller Paradoxien, weshalb es nicht als große Sache angesehen wird.