Warum recherchieren wir nicht mehr nach Kompilierzeitgarantien?

12

Ich mag alles, was Kompilierungszeit ist, und ich mag die Idee, dass, sobald Sie ein Programm kompilieren, eine Menge Garantien für dessen Ausführung gegeben werden. Im Allgemeinen scheint ein statisches Typsystem (Haskell, C ++, ...) stärkere Kompilierungszeitgarantien zu bieten als jedes dynamische Typsystem.

Soweit ich weiß, geht Ada in Bezug auf die Überprüfung der Kompilierungszeit noch weiter und kann vor der Ausführung eine größere Anzahl von Fehlern erkennen. Es wird auch als ziemlich sicher angesehen, da es zu einem bestimmten Zeitpunkt für heikle Felder ausgewählt wurde (wenn Programmierfehler Menschenleben kosten können).

Nun frage ich mich: Wenn stärkere statische Garantien zu mehr dokumentiertem und sichererem Code führen, warum untersuchen wir dann nicht mehr in diese Richtung?

Ein Beispiel für etwas, das zu fehlen scheint, wäre eine Sprache, in der anstelle eines generischen intTyps, dessen Bereich durch die Anzahl der Bits der zugrunde liegenden Architektur bestimmt wird, Bereiche definiert werden könnten (im folgenden Beispiel Int [a..b]wird ein ganzzahliger Typ zwischen zwei angegeben) a und b enthalten):

a : Int [1..24]
b : Int [1..12]
a + b : Int [2..36]
a - b : Int [-11..23]
b - a : Int [-23..11]

oder (dies von Ada nehmen):

a : Int [mod 24]
b : Int [mod 24]
a + b : Int [mod 24]

Diese Sprache würde den besten zugrunde liegenden Typ für den Bereich auswählen und eine Überprüfung der Kompilierzeit für die Ausdrücke durchführen. So ist zum Beispiel gegeben:

a : Int [-3..24]
b : Int [3..10]

dann:

a / b

wird niemals nicht definiert.

Dies ist nur ein Beispiel, aber ich habe das Gefühl, dass wir beim Kompilieren noch viel mehr durchsetzen können. Also, warum scheint es so wenig Forschung zu diesem Thema zu geben? Welche Fachbegriffe beschreiben diese Idee (damit ich weitere Informationen zu diesem Thema finden kann)? Was sind die Grenzen?

Schuh
quelle
2
Pascal hat Integer-Subrange-Typen (dh 1960er Jahre), aber die meisten Implementierungen überprüfen sie leider nur zur Laufzeit (int (-1..4) ist zuweisungskompatibel mit int (100..200) zur Kompilierungszeit). Dies hat nur begrenzte Vorteile, und die vertragsbasierte Programmierung erweitert die Idee in eine bessere Richtung (z. B. Eiffel). Sprachen wie C # versuchen, einige dieser Vorteile mit Attributen zu erzielen. Ich habe sie nicht verwendet, daher bin ich mir nicht sicher, wie nützlich sie in der Praxis sind.
1
@ ӍσӍ: Attribute in C # sind nur Metadatenklassen, sodass eine Datenüberprüfung zur Laufzeit erfolgen würde.
Robert Harvey
8
Woher weißt du, dass es wenig Forschung darüber gibt? Versuchen Sie googeln dependent typeoder refinement type.
Phil
3
Ich stimme zu, dass die Prämisse fehlerhaft zu sein scheint. Dies ist sicherlich ein aktives Forschungsgebiet. Die Arbeit wird nie erledigt . Daher sehe ich nicht ganz ein, wie diese Frage beantwortet werden kann.
Raphael
1
@Robert Harvey: Die Tatsache, dass ADA mehr Garantien bietet, bedeutet nicht, dass der Compiler alle Fehler abfängt, sondern nur, dass Fehler weniger wahrscheinlich sind.
Giorgio

Antworten:

11

Ich bin nicht in der Lage , wie viel zu sagen , mehr Forschung sollte zu dem Thema durchgeführt werden, aber ich kann Ihnen sagen , dass es ist Forschung getan werden, zum Beispiel des Verisoft XT Programm der Deutsch Regierung finanziert.

Die Konzepte, nach denen Sie meines Erachtens suchen, werden als formale Verifizierung und vertragsbasierte Programmierung bezeichnet , wobei letztere eine programmiererfreundliche Möglichkeit darstellt, die erste zu erstellen. Bei der vertragsbasierten Programmierung schreiben Sie zunächst Ihren Code wie gewohnt und fügen dann sogenannte Verträge in den Code ein. Eine leicht verwendbare Sprache, die auf diesem Paradigma basiert, ist Spec # von Microsoft Research und die funktional ähnliche, aber etwas weniger hübsche Code Contracts- Erweiterung für C #, die Sie beide online ausprobieren können (ähnliche Tools für andere Sprachen finden Sie unter rise4fun) ). Der von Ihnen erwähnte "int with range type" würde sich in zwei Verträgen in einer Funktion widerspiegeln:

Contract.Requires(-3 <= a && a <= 24);
Contract.Requires( 3 <= b && b <= 10);

Wenn Sie diese Funktion aufrufen möchten, müssen Sie Parameter verwenden, die diese Kriterien erfüllen. Andernfalls tritt ein Fehler bei der Kompilierung auf. Bei den oben genannten Verträgen handelt es sich um sehr einfache Verträge. Sie können nahezu jede Annahme oder Anforderung zu Variablen oder Ausnahmen und deren Beziehung einfügen, die Sie sich vorstellen können, und der Compiler prüft, ob jede Anforderung durch eine Annahme oder eine zu sichernde, dh ableitbare Bedingung abgedeckt ist aus den Annahmen. Daher kommt der Name: Der Angerufene stellt Anforderungen , der Anrufer stellt sicher, dass diese erfüllt werden - wie in einem Geschäftsvertrag.

P(x1,x2,...,xn)nPwird genutzt. Auf der CS-Seite sind diese beiden Faktoren die kritischen Teile des Prozesses - die Generierung von Verifizierungsbedingungen ist schwierig, und SMT ist je nach den berücksichtigten Theorien entweder ein NP-vollständiges oder ein nicht entscheidbares Problem. Es gibt sogar einen Wettbewerb für SMT-Löser, daher wird hier sicherlich nachgeforscht. Darüber hinaus gibt es alternative Ansätze zur Verwendung von SMT für die formale Verifizierung wie die Aufzählung von Zustandsräumen, die Prüfung symbolischer Modelle, die Prüfung beschränkter Modelle und vieles mehr, die ebenfalls untersucht werden, auch wenn SMT afaik derzeit der "modernste" Ansatz ist.

In Bezug auf die Grenzen der allgemeinen Idee:

  • Wie bereits erwähnt, ist der Nachweis der Korrektheit des Programms ein rechenintensives Problem. Daher kann es sein, dass die Überprüfung der Kompilierungszeit eines Programms mit Verträgen (oder einer anderen Form der Spezifikation) sehr lange dauert oder sogar unmöglich ist. Heuristiken anzuwenden, die die meiste Zeit gut funktionieren, ist das Beste, was man dagegen tun kann.
  • Je mehr Sie über Ihr Programm angeben, desto höher ist die Wahrscheinlichkeit, dass Fehler in der Spezifikation selbst auftreten . Dies kann zu Fehlalarmen führen (die Überprüfung der Kompilierungszeit schlägt fehl, obwohl alles fehlerfrei ist) oder zu dem falschen Eindruck, sicher zu sein, obwohl Ihr Programm immer noch Fehler enthält.
  • Das Schreiben von Verträgen oder Spezifikationen ist wirklich mühsam und die meisten Programmierer sind zu faul, um dies zu tun. Versuchen Sie, überall ein C # -Programm mit Code-Verträgen zu schreiben. Nach einer Weile werden Sie denken: Komm schon, ist das wirklich notwendig? Aus diesem Grund wird die formale Verifizierung in der Regel nur für Hardware-Design und sicherheitskritische Systeme verwendet, z. B. für die Software-Steuerung von Flugzeugen oder Automobilen.

Eine letzte erwähnenswerte Sache, die nicht ganz der obigen Erklärung entspricht, ist ein Feld namens "Implizite Komplexitätstheorie", zum Beispiel dieses Papier . Es zielt darauf ab, Programmiersprachen zu charakterisieren, in denen jedes Programm, das Sie schreiben können, einer bestimmten Komplexitätsklasse zuzuordnen ist, beispielsweise P. In einer solchen Sprache wird automatisch sichergestellt, dass jedes von Ihnen geschriebene Programm eine polynomielle Laufzeit hat, die "überprüft" werden kann. beim kompilieren einfach das programm kompilieren. Ich kenne jedoch keine praktisch verwertbaren Ergebnisse dieser Forschung, bin aber auch weit davon entfernt, ein Experte zu sein.

Stefan Lutz
quelle
Sollte es nicht möglich sein, abhängige Typen oder Verträge aus einer Kombination von Beispieltests und nicht typisiertem Code zu generieren, wenn eine bestimmte "Strategie" vorgegeben ist, die Sie je nach Projekt auswählen können?
aoeu256