Ich interessiere mich für Sprachdesign und kann im Allgemeinen leicht über weithin bekannte Merkmale (z. B. Vererbung, Polymorphismus, Delegierte, Lambdas, Erfassungen, Speicherbereinigung, Ausnahmen, Generika, Varianz, Reflexion usw.) und deren Wechselwirkungen in a nachdenken bestimmte Sprache, die Art und Weise, wie sie möglicherweise implementiert werden können, ihre Einschränkungen usw.
In den letzten Monaten habe ich angefangen, über Rust zu lesen, das über ein Besitzersystem verfügt, das Speichersicherheit und deterministisches Ressourcenmanagement gewährleistet, indem er die statische Überprüfung der Objektlebensdauer erzwingt. Aus der Sicht eines einfachen Benutzers der Sprache konnte ich das System fast sofort abholen.
Aus der Sicht eines Sprachdesigners habe ich jedoch eine Weile gebraucht, um zu erkennen, warum die Dinge in Rust genau so sind, wie sie sind. Ich konnte die Gründe für einige Einschränkungen des Besitzersystems nicht sofort verstehen, bis ich mich zwang, Fälle zu finden, die die Integrität eines Systems verletzen würden, wenn es diese Aspekte nicht hätte.
Meine Hauptfrage hat nichts speziell mit Rust und seinem Eigentum zu tun - aber Sie können sie bei Bedarf als Beispiel in Ihren Kommentaren / Antworten verwenden.
Welche Methodik oder welchen Prozess verwenden Sprachdesigner beim Entwerfen eines neuen Features, um zu entscheiden, ob das Feature ordnungsgemäß funktioniert?
Mit "neu" meine ich, dass es nicht bereits in vorhandenen Sprachen getestet wurde (und daher der Großteil der Arbeit von anderen Designern geleistet wurde). Mit "funktioniert richtig" meine ich, dass die Funktion das beabsichtigte Problem korrekt löst und einigermaßen kugelsicher ist. Mit "einigermaßen kugelsicher" meine ich, dass kein Code in der Sprache oder einer bestimmten Teilmenge der Sprache geschrieben werden kann (z. B. eine Teilmenge ohne "unsicheren" Code), der die Integrität der Funktion verletzen würde.
Handelt es sich um einen Versuch und Irrtum in dem Sinne, dass Sie eine einfache Form der Funktion entwickeln, dann versuchen, Wege zu finden, um sie zu verletzen, und sie dann patchen, wenn Sie sie erfolgreich verletzen, und dann wiederholen? Und wenn Ihnen dann keine anderen möglichen Verstöße einfallen, hoffen Sie, dass nichts mehr übrig ist, und nennen es einen Tag?
Oder gibt es eine formale Möglichkeit, tatsächlich (im mathematischen Sinne des Wortes) zu beweisen, dass Ihr Feature funktioniert, und diesen Beweis dann zu verwenden, um das Feature von Anfang an sicher richtig (oder größtenteils richtig) zu machen?
(Ich sollte erwähnen, dass ich einen technischen Hintergrund habe und keine Informatik. Wenn mir also etwas fehlt, das für CS-Leute offensichtlich ist, können Sie es gerne darauf hinweisen.)
quelle
Antworten:
Ich habe momentan Probleme, die genaue Referenz zu finden, aber vor einiger Zeit habe ich mir mehrere Videos von Simon Peyton Jones angesehen , der maßgeblich zu Haskells Design beigetragen hat. Er ist übrigens ein ausgezeichneter Redner für Typentheorie, Sprachdesign und dergleichen und hat viele Videos kostenlos auf Youtube verfügbar.
Haskell hat eine Zwischendarstellung, die im Wesentlichen aus Lambda-Kalkül besteht und mit ein paar einfachen Dingen ergänzt wird, um die Arbeit zu erleichtern. Lambda-Kalkül wurde verwendet und bewiesen, da ein Computer nur eine Person war, die Dinge berechnete. Ein interessanter Punkt, den Simon Peyton Jones oft macht, ist, dass er, wenn sie etwas Wildes und Verrücktes mit der Sprache machen, weiß, dass es grundsätzlich klingt, wenn es sich schließlich wieder auf diese Zwischensprache reduziert.
Andere Sprachen sind bei weitem nicht so streng, sondern bevorzugen eine einfache Verwendung oder Implementierung. Sie tun dasselbe, was andere Programmierer tun, um qualitativ hochwertigen Code zu erhalten: Befolgen Sie gute Codierungspraktiken und testen Sie ihn zu Tode. Ein Feature wie Rusts Besitzersemantik Ich bin sicher, dass es sowohl eine Menge formaler Analysen als auch Tests gibt, um vergessene Eckfälle zu finden. Oft beginnen solche Funktionen als Abschlussarbeit.
quelle
Also für Sprache Design gibt es Beweise (oder Fehler). Geben Sie beispielsweise Systeme ein. Typen und Programmiersprachen ist das kanonische Buch, das Typensysteme beschreibt und sich darauf konzentriert, die Richtigkeit und Vollständigkeit des Typsystems zu beweisen. Grammatiken haben eine ähnliche Analyse, und Algorithmen (wie das von Ihnen beschriebene Besitzersystem) haben ihre eigenen.
Für die Sprachimplementierung ist es Code wie jeder andere. Sie schreiben Unit-Tests. Sie schreiben Integrationstests. Sie führen Codeüberprüfungen durch.
Das einzige, was Sprachen besonders macht, ist, dass sie (fast immer) unendlich sind. Sie können buchstäblich nicht alle Eingaben testen. Und (im Idealfall) werden sie von Tonnen von Menschen benutzt, die seltsame und interessante Dinge tun, sodass jeder Fehler in der Sprache irgendwann gefunden wird.
In der Praxis verwenden relativ wenige Sprachen Beweise, um ihre Funktionalität zu überprüfen, und erhalten eine Mischung der von Ihnen genannten Optionen.
quelle
The only thing that makes languages special is that they are (almost always) infinite. You literally cannot test all inputs.
Ist das wirklich so besonders? Das scheint mir der übliche Fall zu sein. ZB Eine Funktion, die eine Liste als Argument verwendet, hat auch unendlich viele Eingaben. Für jede Größe n, die Sie auswählen, gibt es eine Liste der Größe n + 1.Das erste und schwierigste, worauf sich ein Sprachdesigner bei der Einführung neuer Funktionen konzentrieren muss, ist, seine Sprache konsistent zu halten:
Um diesbezüglich eine Anleitung zu geben, stützt sich ein Designer auf eine Reihe von Designregeln und -prinzipien. Dieser Ansatz ist in " Das Design und die Entwicklung von C ++ " von Bjarne Stroustrup , einem der seltenen Bücher zum Thema Sprachdesign, sehr gut beschrieben . Was sehr interessant ist, ist zu sehen, dass Sprachen selten im luftleeren Raum entworfen werden, und Designer schauen auch, wie ihre Sprachen ähnliche Funktionen implementiert haben. Eine weitere Quelle (online und kostenlos) sind die Gestaltungsprinzipien für die Java-Sprache .
Wenn Sie sich öffentliche Verfahren von Normungsausschüssen ansehen, werden Sie feststellen, dass es sich eher um einen Prozessfehler handelt. Hier ein Beispiel für ein C ++ - Modul, ein völlig neues Konzept, das in der nächsten Version der Sprache eingeführt werden soll. Und hier eine Analyse, die nach einigen Sprachänderungen erstellt wurde, um den Erfolg zu bewerten. Und hier der Java Community Process , um neue Java-Spezifikationen zu definieren, beispielsweise eine neue API . Sie werden sehen, dass diese Arbeit von mehreren Experten durchgeführt wird, die kreativ ein Konzeptpapier und einen ersten Vorschlag entwerfen. Anschließend werden diese Vorschläge von einer größeren Gemeinschaft / einem größeren Ausschuss geprüft, der den Vorschlag ändern kann, um ein höheres Maß an Kohärenz zu gewährleisten.
quelle
Wie teste ich Programmiersprachenfunktionen? Das ist eine sehr gute Frage, und ich bin mir nicht sicher, ob der Stand der Technik dem Job gewachsen ist.
Jedes neue Feature kann mit allen anderen Features interagieren. (Dies wirkt sich auf Sprache, Dokumente, Compiler, Fehlermeldungen, IDEs, Bibliotheken usw. aus.) Kombinieren Funktionen zusammen eine Lücke? Böse Randfälle erstellen?
Selbst sehr kluge Sprachdesigner, die hart daran arbeiten, die Solidität des Typs aufrechtzuerhalten, entdecken Verstöße wie diesen Rust-Fehler . Das Typsystem von Rust ist für mich nicht so offensichtlich, aber ich denke, in diesem Fall bedeutet die Lebensdauer des Typsystem-Track-Werts, dass die lebenslange "Subtypisierung" (Unterordnungen) mit den Erwartungen an gewöhnliche Subtypisierung, Zwänge, Referenzen und Veränderlichkeit kollidiert und eine Lücke schafft, in der eine
static
Lebensdauer besteht ref kann auf einen vom Stapel zugewiesenen Wert verweisen und später zu einer baumelnden Referenz werden.Für Sprachen, die als Produktionssprachen gedacht sind, dh von vielen Programmierern zum Erstellen zuverlässiger Produktionssoftware verwendet werden, muss "ordnungsgemäß funktionieren" bedeuten, dass das beabsichtigte Problem für das beabsichtigte Publikum korrekt gelöst wird .
Mit anderen Worten, Benutzerfreundlichkeit ist für das Sprachdesign ebenso wichtig wie für andere Arten des Designs. Dies beinhaltet (1) Design für Usability (z. B. Kenntnis Ihrer Zielgruppe) und (2) Usability-Tests.
Ein Beispielartikel zu diesem Thema lautet: „ Programmierer sind auch Menschen, Programmiersprache und API-Designer können viel aus dem Bereich des Human-Factors-Designs lernen.“
Eine Beispiel-SE-Frage zu diesem Thema lautet: Wurde die Syntax einer Programmiersprache auf Benutzerfreundlichkeit getestet?
In einem Beispiel für einen Usability-Test wurde erwogen, eine Listeniterationsfunktion (ich weiß nicht mehr, welche Sprache) zu erweitern, um mehrere Listen zu erstellen. Haben die Leute erwartet, dass es die Listen parallel oder produktübergreifend durchläuft? Die Sprachdesigner waren von den Ergebnissen der Usability-Tests überrascht.
Sprachen wie Smalltalk, Python und Dart wurden mit Schwerpunkt auf Benutzerfreundlichkeit entwickelt. Offensichtlich war Haskell nicht.
quelle