Ich lerne die Programmiersprache Haskell und versuche, mich mit dem Unterschied zwischen a type
und a auseinanderzusetzen kind
.
Wie ich es verstehe, a kind is a type of type
. Zum Beispiel a ford is a type of car
und a car is a kind of vehicle
.
Ist dies eine gute Möglichkeit, darüber nachzudenken?
Denn so wie mein Gehirn gerade verdrahtet ist, a ford is a **type** of car
, aber auch eine car is a **type** of vehicle
Weile gleichzeitig a car is a **kind** of vehicle
. Dh die Begriffe type
und kind
sind austauschbar.
Könnte jemand etwas Licht ins Dunkel bringen?
type-theory
Thomas Cook
quelle
quelle
Antworten:
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 wissen
sqrt (a**2 + b**2)
, die Variablena
undb
werden immer Werte vom Typ seinDouble
und nicht, sagen,String
undBool
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:
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 Beispielf
,a
, undb
), und „Anwendung“ eines Typ Ausdruck zu einem anderen ( zum Beispielf a
wirdf
angewandta
).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:
Wenn der Typausdruck
a -> a
gültig sein soll, welche Arten von Typen sollten wir die Variablen zulassena
? Nun, die Typausdrücke:siehst gültig aus, also die typen
Int
undBool
sind offensichtlich von der richtigen art . Aber auch kompliziertere Typen wie:siehst gültig aus. In der Tat, da wir in der Lage sein sollten,
id
Funktionen aufzurufen , sogar:sieht gut aus. Also
Int
,Bool
,[Double]
,Maybe [(Double,Int)]
, unda -> 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 wieMaybe Int
(in etwa der gleiche Wegsqrt
, für sich genommen, ist ein Wert-Ausdruck genauso gültig wiesqrt 25
). jedoch folgende Typausdruck ist jedoch ungültig: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 wieDouble
oderMaybe [(Double,Int)]
schließt jedoch unvollständige, wertlose Typen wie ausEither 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 Arta
hat Art*
(die Art von Beton - Typen), das Ergebnis der Art Ausdrucksa -> 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 TypInt
und einen zurück Wert von TypInt
, würden wir ihm eine geben Art UnterschriftInt -> Int
, so analog sollten wir gebenMaybe
eine Art Unterschrift* -> *
. GHCi stimmt zu:Zurück gehen zu:
In dieser Typensignatur hat Variable
f
Art* -> *
und Variablena
undb
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, dassa -> b
es sich um einen gültigen Typ mit Art*
handeltf a
und dassf 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 Arta -> 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:
Hier
Bar
ist (sowohl ein Wert als auch) ein Typ. Als Typ ist seine ArtBool -> * -> Foo
eine Funktion auf Typebene, die einen TypBool
(der ein Typ, aber auch eine Art ist) und einen Typ einer Art annimmt und einen Typ einer Art*
erzeugtFoo
. So: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.quelle
type
nur sichtype
selbst, und es würde überhaupt keine Notwendigkeit dafür gebenkind
. Was genau ist der Unterschied?Bool
ist ein TypType
ist eine Art, weil ihre Elemente Typen sindBool -> Int
ist ein TypBool -> Type
ist eine Art, weil ihre Elemente Funktionen sind, die Typen zurückgebenBool * Int
ist ein TypBool * Type
ist eine Art, weil ihre Elemente Paare mit einer Komponente eines Typs sind*
. Somit*
handelt es sich um ein ElementU_1
, das Haskell nicht explizit benennt.quelle
Type :: Type
ist ein Axiom. Die Unterscheidung zwischen "Typ" und "Art" liegt in diesem Fall ausschließlich in der menschlichen Sprache.True
hat einen Typ,Bool
undBool
hat einen TypType
, der selbst Typ hatType
. Manchmal nennen wir einen Typ eine Art, um zu betonen, dass es sich um den Typ einer Entität auf Typebene handelt, in Haskell handelt es sich jedoch immer noch um einen Typ. In einem System , wo Universen tatsächlich existieren, wie Coq, dann „Typ“ kann sich auf ein Universum beziehen und „Art“ zu einem anderen, aber dann in der Regel wollen wir unendlich viele Universen.Type :: Type
und zwischen Typen und Arten zu unterscheiden. Welcher Code wird auchType :: Type
in Haskell demonstriert ?*
in Haskell eine Art Universum gibt. Sie nennen es einfach nicht so.Type
vonData.Kinds
und*
sollten Synonyme sein. Anfangs hatten wir nur*
als Primitiv, während heutzutage das intern wieGHC.Types.Type
im internen ModulGHC.Types
definiert ist, wiederum definiert alstype Type = TYPE LiftedRep
. Ich denke, esTYPE
ist das eigentliche Primitiv, das eine Reihe von Arten bereitstellt (angehobene Typen, unboxed Typen, ...). Der größte Teil der "uneleganten" Komplexität besteht darin, einige Optimierungen auf niedriger Ebene zu unterstützen, und dies nicht aus typentheoretischen Gründen.v
ein Wert ist, dann hat es einen Typ:v :: T
. WennT
ein Typ ist, dann hat es einen Typ:T :: K
. Die Art der Art heißt Art. Typen, die aussehen,TYPE rep
können als Sorten bezeichnet werden, obwohl das Wort ungewöhnlich ist. IffT :: TYPE rep
wirdT
auf der rechten Seite von a erscheinen dürfen::
. Das Wort "Art" hat eine Nuance:K
InT :: K
ist eine Art, aber nicht inv :: K
, obwohl es das gleiche istK
. Wir könnten definieren "K
ist eine Art, wenn ihre Art eine Art ist" oder "Arten sind auf der rechten Seite von::
", aber das erfasst die Verwendung nicht richtig. Daher meine "menschliche Auszeichnung" Position.EIN Wert ist wie der spezifische rote 2011 Ford Mustang mit 19.206 Meilen darauf, den Sie in Ihrer Fahrstraße sitzen haben.
Informell könnte dieser spezifische Wert viele haben Arten haben : Es ist ein Mustang und es ist ein Ford und es ist ein Auto und es ist ein Fahrzeug, neben vielen anderen Arten, die man erfinden könnte (die Art von "Dingen") Ihnen gehören ", oder die Art der" Dinge, die rot sind ", oder ...).
(In Haskell haben Werte in erster Näherung (GADTs unterbrechen diese Eigenschaft, und die Magie um Zahlenliterale und die OverloadedStrings-Erweiterung verdecken sie ein wenig) einen Haupttyp anstelle der Fülle informeller "Typen", die Sie angeben können. ' stach.
42
ist im Sinne dieser Erklärung einInt
; es gibt in Haskell keinen Typ für "Zahlen" oder "sogar ganze Zahlen" - oder Sie könnten einen machen, aber es wäre ein disjunkter Typ vonInt
.)Jetzt kann "Mustang" ein Untertyp von "Auto" sein - jeder Wert, der ein Mustang ist, ist auch ein Auto. Aber der Typ - oder, um Haskells Terminologie zu verwenden, ist die Art von "Mustang" nicht "Auto". "Mustang" der Typ ist keine Sache, die Sie in Ihrer Einfahrt parken oder in der Sie herumfahren können. "Mustang" ist ein Substantiv oder eine Kategorie oder nur ein Typ. Das sind informell die Arten von "Mustang".
(Wiederum erkennt Haskell nur eine Art für jede Sache auf Typebene. Also
Int
hat Art*
und keine anderen Arten.Maybe
Hat Art* -> *
und keine anderen Arten. Aber die Intuition sollte immer noch halten:42
ist eineInt
, und Sie könnenInt
Dinge damit tun . wie Addieren und SubtrahierenInt
selbst ist nichtInt
, es als eine solche Zahl istInt + Int
Sie können formlos Leute sagen hören , dass.Int
eine istNum
, durch die sie bedeuten , dass ein dort ist beispielsweise derNum
Typ - Klasse für den TypInt
-Das ist nicht das gleiche als zu sagen, dassInt
hat ArtNum
.Int
hat Art "Typ", die in Haskell geschrieben ist*
.)Ist also nicht jeder informelle "Typ" nur ein Substantiv oder eine Kategorie? Haben alle Typen die gleiche Art? Warum überhaupt über Arten reden, wenn sie so langweilig sind?
Dies ist der Punkt, an dem die englische Analogie ein wenig steiniger wird, aber bedenken Sie: Stellen Sie sich vor, dass das Wort "Eigentümer" auf Englisch für sich genommen keinen Sinn ergibt, ohne dass eine Beschreibung des Eigentums vorliegt. Stellen Sie sich vor, dass jemand, der Sie "Eigentümer" nennt, für Sie überhaupt keinen Sinn ergibt. Aber wenn dich jemand einen "Autobesitzer" nennt, kannst du verstehen, was er meint.
"Besitzer" hat nicht die gleiche Art wie "Auto", weil man in dieser erfundenen Version des Englischen nicht über ein Auto sprechen kann, aber über einen Besitzer. Man kann nur von einem "Autobesitzer" sprechen. "Eigentümer" erstellt nur dann ein "Nomen", wenn es auf etwas angewendet wird, das bereits ein "Nomen" hat, wie "Auto". Wir würden sagen, dass die Art von "Eigentümer" "Nomen -> Nomen" ist. "Owner" ist wie eine Funktion, die ein Nomen annimmt und daraus ein anderes Nomen erzeugt; Aber es ist kein Substantiv.
Beachten Sie, dass "Autobesitzer" kein Untertyp von "Auto" ist! Es ist keine Funktion, die Autos akzeptiert oder zurückgibt! Es ist nur eine völlig andere Art von "Auto". Es beschreibt Werte mit zwei Armen und zwei Beinen, die zu einem bestimmten Zeitpunkt einen bestimmten Geldbetrag hatten und dieses Geld zu einem Händler brachten. Es werden keine Werte beschrieben, die über vier Räder und eine Lackierung verfügen. Beachten Sie auch, dass "Autobesitzer" und "Hundebesitzer" unterschiedliche Typen sind und Dinge, die Sie mit einem machen möchten, möglicherweise nicht auf den anderen zutreffen.
(Ebenso, wenn wir in Haskell sagen, dass das
Maybe
eine Art hat* -> *
, ist es unsinnig (formal; informell tun wir es die ganze Zeit), über "einMaybe
" zu sprechen . Stattdessen können wir einMaybe Int
oder ein habenMaybe String
, da dies Dinge von sind Art*
.)Es geht also darum, überhaupt über Arten zu sprechen, damit wir unsere Argumentation in Bezug auf Wörter wie "Eigentümer" formalisieren und durchsetzen können, dass wir immer nur Werte von Typen verwenden, die "vollständig konstruiert" und nicht unsinnig sind.
quelle
Das ist richtig - also lasst uns untersuchen, was das bedeutet.
Int
oderText
sind konkrete Typen, aberMaybe a
ein abstrakter Typ. Es wird erst dann zu einem konkreten Typ, wenn Sie entscheiden, welchen bestimmten Wert Siea
für eine bestimmte Variable (oder Wert / Ausdruck / was auch immer) möchten, zMaybe Text
.Wir sagen, das
Maybe a
ist ein Typkonstruktor, weil es wie eine Funktion ist, die einen einzelnen konkreten Typ annimmt (z. B.Text
) und einen konkreten Typ zurückgibt (Maybe Text
in diesem Fall). Andere Typkonstruktoren benötigen jedoch möglicherweise noch mehr "Eingabe-Parameter", bevor sie einen konkreten Typ zurückgeben. ZBMap k v
müssen zwei konkrete Typen (z. B.Int
undText
) verwendet werden, bevor ein konkreter Typ (Map Int Text
) erstellt werden kann.Die Konstruktoren
Maybe a
undList a
type haben also dieselbe "Signatur", die wir bezeichnen* -> *
(ähnlich wie die Haskell-Funktionssignatur), da sie, wenn Sie ihnen einen konkreten Typ geben, einen konkreten Typ ausspucken. Wir nennen dies die "Art" des TypsMaybe
undList
haben die gleiche Art.Die konkreten Typen haben eine Art
*
, und unser Kartenbeispiel ist eine Art,* -> * -> *
da zwei konkrete Typen als Eingabe verwendet werden, bevor ein konkreter Typ ausgegeben werden kann.Sie können sehen , es ist meist nur über die Anzahl der „Parameter“ , dass wir in die Art Konstruktor übergeben - aber erkennen , dass wir auch innerhalb Typkonstruktoren verschachtelt Typkonstruktoren erhalten können, so dass wir mit einer Art können am Ende , dass sieht aus wie
* -> (* -> *) -> *
zum Beispiel .Wenn Sie ein Scala / Java-Entwickler sind, finden Sie diese Erklärung möglicherweise auch hilfreich: https://www.atlassian.com/blog/archives/scala-types-of-a-higher-kind
quelle
Maybe a
einem Synonym fürforall a. Maybe a
einen polymorphen Typ*
undMaybe
einem monomorphen Typ* -> *
.