Was tun Sprachdesigner, um zu entscheiden oder zu beweisen, dass eine bestimmte Funktion ordnungsgemäß funktioniert?

11

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.)

Theodoros Chatzigiannakis
quelle
Wenn Sie "Sprachdesigner" sagen, meinen Sie damit eine Person, die den Compiler oder nur die Syntax oder beides erstellt?
Snoop
6
@StevieV: Language Design ist anders und unabhängig von der Implementierung. Zum Beispiel wurde Lisp von John McCarthy als leicht verständliche Alternative zum λ-Kalkül entworfen. Er hat es jedoch nicht umgesetzt. Als sein Schüler Steve Russell Lisp implementieren wollte, sagte McCarthy ihm, dass er glaubte, es sei unmöglich, Lisp zu implementieren! APL wurde als Sprache für den Mathematikunterricht entwickelt. Später verwendete IBM es, um das Verhalten des System / 360 formal festzulegen, für das die Sprache mehrere Erweiterungen erhielt. Zu diesem Zeitpunkt war es noch nicht implementiert. Plankalkül wurde von Konrad
Jörg W Mittag am
4
Zuse 1942-1946, aber erst 1975 implementiert. Niklaus Wirth entwarf seine Sprachen zuerst vollständig und implementierte sie erst, nachdem er mit dem Entwurf fertig war (und er schrieb den ersten Compiler in der Sprache selbst, um ein Gefühl dafür zu bekommen, wie gut die Sprache war entworfen - dann ließ er seine Schüler den Compiler zum Bootstrapping von Hand in eine andere Sprache übersetzen). Viele weitere akademische Sprachen werden nie implementiert, sondern dienen nur dazu, einen Punkt zu beweisen oder auf abstrakte Weise mit bestimmten Sprachmerkmalen zu experimentieren. Smalltalk wurde als Ergebnis einer gegenseitigen Wette erstellt: Alan Kay setzte darauf, dass er könnte
Jörg W Mittag
3
Dan Ingalls entwarf eine objektorientierte Sprache auf einer einzigen Seite Papier und setzte darauf, dass er diese Sprache in ein paar Tagen implementieren könnte. (Und er hat es ausgerechnet in BASIC gemacht!) Sprachen sind mathematische Objekte, die unabhängig von ihren Compilern / Interpreten existieren. Und sie können unabhängig von einer physischen Implementierung entworfen, untersucht und diskutiert werden.
Jörg W Mittag
3
Muss lesen: Gödel, Escher, Bach . Es ist manchmal etwas seltsam, aber gegen Ende wird ein Großteil von Turing & Godels Arbeiten behandelt, die die Formalität des Sprachdesigns stark beeinflussen.
RubberDuck

Antworten:

6

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.

Karl Bielefeldt
quelle
2
Ich glaube , die Referenz Sie suchen in einem der „Abenteuer mit Typen in Haskell“ -Serie ist, wahrscheinlich diese den Inhalt der Platte in der Miniaturansicht gegeben ...
Jules
8

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.

Telastyn
quelle
4
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.
Doval
@doval - und Strings auch, nehme ich an. Ein guter Punkt.
Telastyn
4

Das erste und schwierigste, worauf sich ein Sprachdesigner bei der Einführung neuer Funktionen konzentrieren muss, ist, seine Sprache konsistent zu halten:

  • Wie kann es in die Sprachgrammatik integriert werden, ohne vorhandenen Code zu beschädigen (dies kann mathematisch bewiesen werden)?
  • Wie es sich auf vorhandene Features bezieht (wenn Sie beispielsweise feste Arrays mit dem Index 0..n-1 haben, werden Sie kein neues Feature mit variablen Arrays mit dem Index 1..n einführen) (das ist der künstlerische Teil des Designs).
  • Wie das Feature in der gesamten Toolchain implementiert werden kann, damit das neue Feature vom Ökosystem, den Toolmakers und den Programmierern übernommen werden kann (die Machbarkeit kann mit einem Proof of Concept demonstriert werden, aber die vollständige Implementierung ist ein Ansatz ähnlich dem Programmieren).

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.

Christophe
quelle
4

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 staticLebensdauer besteht ref kann auf einen vom Stapel zugewiesenen Wert verweisen und später zu einer baumelnden Referenz werden.

Mit "funktioniert richtig" meine ich, dass die Funktion das beabsichtigte Problem korrekt löst und einigermaßen kugelsicher ist.

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.

Jerry101
quelle
Haskell ist eigentlich verdammt brauchbar. Es ist nur schwer zu lernen, weil es ein völlig anderes Paradigma als Python / C / Java usw. ist. Aber als Sprache ist es ziemlich einfach zu benutzen.
Semikolon