Es ist allgemein anerkannt, dass Java-Generika auf einige wichtige Arten versagt haben. Die Kombination von Platzhaltern und Schranken führte zu ernsthaft unlesbarem Code.
Wenn ich mir jedoch andere Sprachen anschaue, kann ich anscheinend kein generisches Typsystem finden, mit dem Programmierer zufrieden sind.
Nehmen wir als Entwurfsziele eines solchen Typsystems:
- Erzeugt immer leicht lesbare Typdeklarationen
- Einfach zu erlernen (Kovarianz, Kontravarianz usw. müssen nicht aufgefrischt werden)
- Maximiert die Anzahl der Fehler bei der Kompilierung
Gibt es eine Sprache, die es richtig macht? Wenn ich google, sehe ich nur Beschwerden darüber, wie das Typsystem in der Sprache X funktioniert. Ist diese Komplexität mit der generischen Typisierung verbunden? Sollten wir einfach aufhören, die Typensicherheit bei der Kompilierung zu 100% zu überprüfen?
Meine Hauptfrage ist, in welcher Sprache diese drei Ziele am besten erreicht wurden. Mir ist klar, dass das subjektiv ist, aber ich kann noch nicht einmal eine Sprache finden, in der nicht alle Programmierer der Meinung sind, dass das generische Typensystem ein Chaos ist.
Nachtrag: Wie bereits erwähnt, ist die Kombination von Subtypisierung / Vererbung und Generika das, was die Komplexität erzeugt. Daher suche ich wirklich nach einer Sprache, die beides kombiniert und die Explosion der Komplexität vermeidet.
quelle
easy-to-read type declarations
? Das dritte Kriterium ist ebenfalls nicht eindeutig: Beispielsweise kann ich Array-Index-Ausnahmen außerhalb der Grenzen in Fehler bei der Kompilierung umwandeln, indem Sie keine Arrays indexieren lassen, es sei denn, ich kann den Index zur Kompilierungszeit berechnen. Das zweite Kriterium schließt auch die Untertypisierung aus. Das ist nicht unbedingt eine schlechte Sache, aber Sie sollten sich dessen bewusst sein, was Sie fragen.Foo<T> where SiameseCat:T
), und dass es keine Möglichkeit gibt, einen generischen Typ zu haben, der nicht konvertierbar istObject
. Meiner Meinung nach würde .NET von Aggregattypen profitieren, die strukturähnlich, aber noch bloßer sind. WennKeyValuePair<TKey,TValue>
es sich um einen solchen Typ handelt, kann auf einen umgewandeltIEnumerable<KeyValuePair<SiameseCat,FordFocus>>
werdenIEnumerable<KeyValuePair<Animal,Vehicle>>
, jedoch nur, wenn der Typ nicht in ein Kästchen eingeschlossen werden kann.Antworten:
Während Generika seit Jahrzehnten ein fester Bestandteil der Community der funktionalen Programmierung sind, bietet das Hinzufügen von Generika zu objektorientierten Programmiersprachen einige einzigartige Herausforderungen, insbesondere das Zusammenspiel von Untertypen und Generika.
Selbst wenn wir uns auf objektorientierte Programmiersprachen und insbesondere auf Java konzentrieren, hätte ein weitaus besseres generisches System entworfen werden können:
Generische Typen sollten überall dort zulässig sein, wo es andere Typen gibt. Insbesondere wenn
T
es sich um einen Typparameter handelt, sollten die folgenden Ausdrücke ohne Warnungen kompiliert werden:Ja, dazu müssen Generika wie jeder andere Typ in der Sprache angepasst werden.
Kovarianz und Kontravarianz eines generischen Typs sollten in seiner Deklaration angegeben (oder daraus abgeleitet) werden, anstatt jedes Mal, wenn der generische Typ verwendet wird, damit wir schreiben können
eher, als
Da generische Typen ziemlich lang werden können, sollten wir sie nicht redundant angeben müssen. Das heißt, wir sollten schreiben können
eher, als
Als Typparameter sollte jeder Typ zulässig sein, nicht nur Referenztypen. (Wenn wir eine haben können
int[]
, warum können wir keine habenList<int>
)?All dies ist in C # möglich.
quelle
Die Verwendung von Subtypen verursacht beim generischen Programmieren viele Komplikationen. Wenn Sie darauf bestehen, eine Sprache mit Untertypen zu verwenden, müssen Sie akzeptieren, dass die damit einhergehende generische Programmierung eine gewisse Komplexität aufweist. Einige Sprachen können es besser als andere, aber Sie können es nur so weit bringen.
Vergleichen Sie das zum Beispiel mit Haskells Generika. Sie sind einfach genug , dass , wenn Sie Typinferenz verwenden, können Sie eine korrekte generische Funktion von Schreib Unfall . In der Tat, wenn Sie einen einzigen Typ angeben, oft der Compiler selbst sagt : „Nun, ich war im Begriff , dieses generisch zu machen, aber man hat mich gebeten , es für ints nur zu machen, so was auch immer.“
Zugegeben, die Leute verwenden Haskells Schriftsystem auf erstaunlich komplexe Weise, was es zum Fluch eines jeden Neulings macht, aber das zugrunde liegende Schriftsystem selbst ist elegant und sehr bewundert.
quelle
a
muss eine Art Ganzzahl sein".Vor etwa 20 Jahren wurde eine ganze Reihe von Forschungen angestellt, um Generika mit Subtypisierung zu kombinieren. In der Programmiersprache Thor, die von Barbara Liskovs Forschungsgruppe am MIT entwickelt wurde, gab es "where" -Klauseln, mit denen Sie die Anforderungen des Typs angeben können, über den Sie parametrisieren. (Dies ähnelt dem, was C ++ mit Concepts versucht .)
Der Artikel, der Thors Generika beschreibt und wie sie mit Thors Untertypen interagieren, lautet: Day, M; Gruber, R; Liskov, B; Myers, AC: Subtypes vs. where-Klauseln: Einschränkung des parametrischen Polymorphismus , ACM Conf on Obj-Oriented Prog, Sys, Lang und Apps , (OOPSLA-10): 156-158, 1995.
Ich glaube, dass sie ihrerseits auf Arbeiten aufbauen, die Ende der 1980er Jahre an Emerald ausgeführt wurden. (Ich habe diese Arbeit nicht gelesen, aber die Referenz ist: Black, A; Hutchinson, N; Jul, E; Levy, H; Carter, L: Verteilung und abstrakte Typen in Emerald , _IEEE T. Software Eng., 13 ( 1): 65-76, 1987.
Sowohl Thor als auch Emerald waren "akademische Sprachen", so dass sie wahrscheinlich nicht genug gebraucht wurden, um wirklich zu verstehen, ob Where-Klauseln (Konzepte) wirklich echte Probleme lösen. Es ist interessant, Bjarne Stroustrups Artikel darüber zu lesen, warum der erste Versuch mit Concepts in C ++ fehlgeschlagen ist: Stroustrup, B: Die C ++ 0x-Entscheidung "Konzepte entfernen" , Dr. Dobbs , 22. Juli 2009. (Weitere Informationen auf der Homepage von Stroustrup . )
Eine andere Richtung, die die Leute zu versuchen scheinen, nennt man Eigenschaften . Zum Beispiel verwendet Mozillas Programmiersprache Rust Merkmale. So wie ich es verstehe (was möglicherweise völlig falsch ist), ist die Feststellung, dass eine Klasse ein Merkmal erfüllt, fast so, als würde man sagen, dass eine Klasse eine Schnittstelle implementiert. Es scheint, dass Apples neue Swift-Programmiersprachen ein ähnliches Konzept von Protokollen verwenden , um Einschränkungen der Parameter für Generika festzulegen .
quelle