it-swarm.com.de

Was ist ein Typensystem?

Hintergrund

Ich entwerfe eine Sprache als Nebenprojekt. Ich habe einen funktionierenden Assembler, einen statischen Analysator und eine virtuelle Maschine dafür. Da ich mit der von mir aufgebauten Infrastruktur bereits nicht triviale Programme kompilieren und ausführen kann, habe ich mir überlegt, an meiner Universität eine Präsentation zu halten.

Während meines Vortrags erwähnte ich, dass die VM ein Typensystem bereitstellt, wurde gefragt "Wofür ist Ihr Typensystem?". Nach der Antwort wurde ich von der Person ausgelacht die Frage stellen.

Obwohl ich mit ziemlicher Sicherheit den Ruf verlieren werde, diese Frage zu stellen, wende ich mich an Programmierer.

Mein Verständnis

Nach meinem Verständnis werden Typsysteme verwendet, um zusätzliche Informationsebenen über Entitäten in einem Programm bereitzustellen, sodass die Laufzeit, der Compiler oder eine andere Maschine weiß, was mit den Bitfolgen zu tun ist, mit denen sie arbeitet. Sie helfen auch bei der Pflege von Verträgen - der Compiler (oder der Code-Analysator oder die Laufzeit oder ein anderes Programm) kann überprüfen, ob das Programm zu einem bestimmten Zeitpunkt mit Werten arbeitet, die von Programmierern erwartet werden.

Typen können auch verwendet werden, um Informationen für diese menschlichen Programmierer bereitzustellen. Zum Beispiel finde ich diese Erklärung:

function sqrt(double n) -> double;

nützlicher als dieser

sqrt(n)

Ersteres gibt viele Informationen: dass der Bezeichner sqrt eine Funktion ist, ein einzelnes double als Eingabe verwendet und ein weiteres double als Ausgabe erzeugt. Letzteres sagt Ihnen, dass es sich wahrscheinlich um eine Funktion handelt, die einen einzelnen Parameter verwendet.

Meine Antwort

Also, nachdem Sie gefragt wurden "Wofür ist Ihr Typensystem?" Ich antwortete wie folgt:

Das Typsystem ist dynamisch (Typen werden Werten zugewiesen, nicht Variablen, die sie enthalten), aber stark ohne überraschende Zwangsregeln (Sie können der Ganzzahl keine Zeichenfolge hinzufügen, da sie inkompatible Typen darstellen, aber Sie können der Gleitkommazahl eine Ganzzahl hinzufügen). .

Das Typsystem wird von VM verwendet, um sicherzustellen, dass Operanden für Anweisungen gültig sind; und kann von Programmierern verwendet werden, um sicherzustellen, dass an ihre Funktionen übergebene Parameter gültig sind (d. H. Vom richtigen Typ).
Das Typsystem unterstützt Subtypisierung und Mehrfachvererbung (beide Funktionen stehen Programmierern zur Verfügung), und Typen werden berücksichtigt, wenn der dynamische Versand von Methoden für Objekte verwendet wird - VM verwendet Typen zur Überprüfung Durch welche Funktion wird eine bestimmte Nachricht für einen bestimmten Typ implementiert?.

Die Folgefrage lautete "Und wie wird der Typ einem Wert zugewiesen?". Daher erklärte ich, dass alle Werte in einem Kästchen stehen und einen Zeiger auf eine Typdefinitionsstruktur haben, der Informationen über den Namen des Typs, die Nachrichten, auf die er antwortet, und die Typen, von denen er erbt, enthält.

Danach wurde ich ausgelacht und meine Antwort mit dem Kommentar "Das ist kein echtes Typensystem" abgewiesen.

Also - wenn das, was ich beschrieben habe, nicht als "echtes Typensystem" qualifiziert ist, was würde das? Hatte diese Person Recht, dass das, was ich zur Verfügung stelle, nicht als Typensystem angesehen werden kann?

51
Mael

Das alles scheint eine gute Beschreibung dessen zu sein, was Typsysteme bieten. Und Ihre Implementierung klingt vernünftig genug für das, was sie tut.

Für einige Sprachen benötigen Sie keine Laufzeitinformationen, da Ihre Sprache keinen Laufzeitversand ausführt (oder Sie führen einen Einzelversand über vtables oder einen anderen Mechanismus durch, benötigen also keine Typinformationen). Für einige Sprachen reicht es aus, nur ein Symbol/einen Platzhalter zu haben, da Sie sich nur um die Typgleichheit kümmern, nicht um den Namen oder die Vererbung.

Abhängig von Ihrer Umgebung hat sich die Person möglicherweise mehr Formalismus in Ihrem Typensystem gewünscht. Sie wollen wissen, was Sie damit beweisen können , nicht was Programmierer können -) damit. Dies ist in der Wissenschaft leider ziemlich häufig. Obwohl Akademiker solche Dinge tun, weil es ziemlich leicht ist, Fehler in Ihrem Typensystem zu haben, die es den Dingen ermöglichen, der Korrektheit zu entgehen. Es ist möglich, dass sie eine davon entdeckt haben.

Wenn Sie weitere Fragen hatten, ist Typen und Programmiersprachen das kanonische Buch zu diesem Thema und kann Ihnen helfen, einige der von Akademikern benötigten Strenge sowie einige Begriffe zur Beschreibung der Dinge zu lernen.

31
Telastyn

Ich mag die Antwort von @ Telastyn besonders wegen des Hinweises auf das akademische Interesse am Formalismus.

Gestatten Sie mir, der Diskussion etwas hinzuzufügen.

Was ist ein Typensystem?

Ein Typsystem ist ein Mechanismus zum Definieren, Erkennen und Verhindern illegaler Programmzustände. Es funktioniert durch Definieren und Anwenden von Einschränkungen. Die Einschränkungsdefinitionen sind Typen und die Einschränkungsanwendungen sind Verwendung von Typen, z. in Variablendeklaration.

Typdefinitionen unterstützen typischerweise Kompositionsoperatoren (z. B. verschiedene Formen der Konjunktion, wie in Strukturen, Unterklassen und Disjunktion, wie in Aufzählungen, Gewerkschaften).

Die Einschränkungen, Verwendungen von Typen, erlauben manchmal auch Kompositionsoperatoren (z. B. zumindest dies, genau dies, entweder dies oder das, vorausgesetzt, dass etwas anderes gilt).

Wenn das Typsystem in der Sprache verfügbar ist und zur Kompilierungszeit angewendet wird, um Fehler bei der Kompilierung ausgeben zu können, handelt es sich um ein statisches Typsystem. Diese verhindern, dass viele illegale Programme kompiliert werden, geschweige denn ausgeführt werden, und verhindern daher illegale Programmzustände.

(Ein statisches Typsystem verhindert, dass ein Programm ausgeführt wird, unabhängig davon, ob bekannt (oder unentscheidbar) ist, dass das Programm jemals den fehlerhaften Code erreicht, über den es sich beschwert. Ein statisches Typsystem erkennt bestimmte Arten von Unsinn (Verstöße gegen die deklarierten Einschränkungen). und beurteilt das Programm als fehlerhaft, bevor es jemals ausgeführt wird.)

Wenn ein Typsystem zur Laufzeit angewendet wird, handelt es sich um ein dynamisches Typsystem, das unzulässige Programmzustände verhindert. Es stoppt das Programm jedoch während der Ausführung, anstatt zu verhindern, dass es überhaupt ausgeführt wird.

Ein ziemlich verbreitetes Systemangebot besteht darin, sowohl statische als auch dynamische Funktionen bereitzustellen.

20
Erik Eidt

Oh Mann, ich freue mich darauf, diese Frage so gut wie möglich zu beantworten. Ich hoffe, ich kann meine Gedanken richtig ordnen.

Wie @Doval erwähnte und der Fragesteller darauf hinwies (wenn auch grob), haben Sie nicht wirklich ein Typensystem. Sie haben ein System dynamischer Überprüfungen mit Tags, das im Allgemeinen viel schwächer und auch viel weniger interessant ist.

Die Frage "Was ist ein Typensystem?" Kann sehr philosophisch sein, und wir könnten ein Buch mit unterschiedlichen Standpunkten zu diesem Thema füllen. Da dies jedoch eine Seite für Programmierer ist, werde ich versuchen, meine Antwort so praktisch wie möglich zu halten (und tatsächlich sind Typen extrem praktisch in der Programmierung , trotz allem, was manche denken mögen).

Überblick

Beginnen wir mit einem "Sitz der Hosen", um zu verstehen, wozu ein Typensystem gut ist, bevor wir uns mit den formaleren Grundlagen befassen. Ein Typsystem legt unseren Programmen Struktur auf. Sie sagen uns, wie wir verschiedene Funktionen und Ausdrücke miteinander verbinden können. Ohne Struktur sind Programme unhaltbar und äußerst komplex und können beim geringsten Fehler des Programmierers Schaden anrichten.

Das Schreiben von Programmen mit einem Typensystem ist wie das Fahren einer Pflege in neuwertigem Zustand - die Bremsen funktionieren, die Türen schließen sicher, der Motor ist geölt usw. Das Schreiben von Programmen ohne Typsystem ist wie das Fahren eines Motorrads ohne Helm und mit Rädern aus Spaghetti. Sie haben absolut keine Kontrolle über Ihre.

Nehmen wir an, wir haben eine Sprache mit den wörtlichen Ausdrücken num[n] Und str[s], Die die Ziffer n bzw. die Zeichenfolge s und die primitiven Funktionen plus und concat mit der beabsichtigten Bedeutung. Natürlich möchten Sie nicht in der Lage sein, etwas wie plus "hello" "world" Oder concat 2 4 Zu schreiben. Aber wie können wir das verhindern? A priori gibt es keine Methode, um die Ziffer 2 vom Zeichenfolgenliteral "Welt" zu unterscheiden. Wir möchten sagen, dass diese Ausdrücke in verschiedenen Kontexten verwendet werden sollten. Sie haben verschiedene Arten.

Sprachen und Typen

Lassen Sie uns einen Schritt zurücktreten: Was ist eine Programmiersprache? Im Allgemeinen können wir eine Programmiersprache in zwei Ebenen unterteilen: die Syntax und die Semantik. Diese werden auch als Statik bzw. Dynamik bezeichnet. Es stellt sich heraus, dass das Typsystem notwendig ist, um die Interaktion zwischen diesen beiden Teilen zu vermitteln.

Syntax

Ein Programm ist ein Baum. Lassen Sie sich nicht von den Textzeilen täuschen, die Sie auf einem Computer schreiben. Dies sind nur die vom Menschen lesbaren Darstellungen eines Programms. Das Programm selbst ist ein abstrakter Syntaxbaum . Zum Beispiel könnten wir in C schreiben:

int square(int x) { 
    return x * x;
 }

Das ist die konkrete Syntax für das Programm (Fragment). Die Baumdarstellung lautet:

     function square
     /     |       \
   int   int x    return
                     |
                   times
                  /    \
                 x      x

Eine Programmiersprache liefert eine Grammatik , die die gültigen Bäume dieser Sprache definiert ( Es kann entweder eine konkrete oder eine abstrakte Syntax verwendet werden. Dies geschieht normalerweise mit der BNF-Notation. Ich würde annehmen, dass Sie dies für die von Ihnen erstellte Sprache getan haben.

Semantik

OK, wir wissen, was ein Programm ist, aber es ist nur eine statische Baumstruktur. Vermutlich wollen wir, dass unser Programm tatsächlich etwas berechnet . Wir brauchen Semantik.

Die Semantik von Programmiersprachen ist ein reiches Fachgebiet. Grundsätzlich gibt es zwei Ansätze: Denotationssemantik und operationelle Semantik . Die Denotationssemantik beschreibt ein Programm, indem es in eine zugrunde liegende mathematische Struktur abgebildet wird (z. B. die natürlichen Zahlen, stetigen Funktionen usw.). das gibt unserem Programm Sinn. Im Gegensatz dazu definiert die Betriebssemantik ein Programm, indem es detailliert beschreibt, wie es ausgeführt wird. Meiner Meinung nach ist die operative Semantik für Programmierer (einschließlich meiner selbst) intuitiver. Bleiben wir also dabei.

Ich werde nicht durchgehen, wie man eine formale operative Semantik definiert (die Details sind etwas kompliziert), aber im Grunde wollen wir Regeln wie die folgenden:

  1. num[n] Ist ein Wert
  2. str[s] Ist ein Wert
  3. Wenn num[n1] Und num[n2] Zu den ganzen Zahlen ausgewertet werden n_1$ and $n_2$, then Plus (num [n1], num [n2]) `ergibt die ganze Zahl $ n_1 + n_2 $.
  4. Wenn str[s1] Und str[s2] Die Zeichenfolgen s1 und s2 auswerten, wird concat(str[s1], str[s2]) die Zeichenfolge s1s2 ausgewertet.

Usw. Die Regeln sind in der Praxis viel formeller, aber Sie bekommen den Kern. Wir stoßen jedoch bald auf ein Problem. Was passiert, wenn wir Folgendes schreiben:

concat(num[5], str[hello])

Hm. Das ist ein ziemliches Rätsel. Wir haben nirgendwo eine Regel definiert, wie eine Zahl mit einer Zeichenfolge verkettet werden soll. Wir könnten versuchen, eine solche Regel zu erstellen, aber wir wissen intuitiv, dass diese Operation bedeutungslos ist. Wir möchten nicht, dass dieses Programm gültig ist. Und so werden wir unaufhaltsam zu Typen geführt.

Typen

Ein Programm ist ein Baum, wie er durch die Grammatik einer Sprache definiert ist. Programme erhalten durch Ausführungsregeln eine Bedeutung. Einige Programme können jedoch nicht ausgeführt werden. Das heißt, einige Programme sind bedeutungslos . Diese Programme sind schlecht getippt. Das Tippen kennzeichnet also sinnvolle Programme in einer Sprache. Wenn ein Programm gut typisiert ist, können wir es ausführen.

Lassen Sie uns einige Beispiele geben. Wie bei den Bewertungsregeln werde ich auch hier die Schreibregeln informell präsentieren, aber sie können streng gestaltet werden. Hier sind einige Regeln:

  1. Ein Token der Form num[n] Hat den Typ nat.
  2. Ein Token der Form str[s] Hat den Typ str.
  3. Wenn der Ausdruck e1 Den Typ nat und der Ausdruck e2 Den Typ nat hat, hat der Ausdruck plus(e1, e2) den Typ nat.
  4. Wenn Ausdruck e1 Typ str und Ausdruck e2 Typ str hat, hat Ausdruck concat(e1, e2) Typ str .

Nach diesen Regeln gibt es also plus(num[5], num[2]) vom Typ nat, aber wir können plus(num[5], str["hello"]) keinen Typ zuweisen. Wir sagen, ein Programm (oder Ausdruck) ist gut typisiert, wenn wir ihm einen beliebigen Typ zuweisen können, und es ist ansonsten schlecht typisiert. Ein Typsystem ist Sound , wenn alle gut typisierten Programme ausgeführt werden können. Haskell ist gesund; C ist nicht.

Fazit

Es gibt andere Ansichten zu Typen. Typen entsprechen in gewissem Sinne der intuitionistischen Logik und können auch als Objekte in der Kategorietheorie angesehen werden. Das Verständnis dieser Zusammenhänge ist faszinierend, aber es ist nicht wesentlich, wenn man nur eine Programmiersprache schreiben oder sogar entwerfen möchte. Das Verständnis von Typen als Werkzeug zur Steuerung von Programmformationen ist jedoch für das Design und die Entwicklung von Programmiersprachen von wesentlicher Bedeutung. Ich habe nur die Oberfläche zerkratzt, was Typen ausdrücken können. Ich hoffe, Sie denken, dass sie sich lohnen, um sie in Ihre Sprache zu integrieren.

14
gardenhead