Allmähliche Typisierung: „Fast jede Sprache mit einem statischen Typsystem hat auch ein dynamisches Typsystem“

20

Diese Behauptung von Aleks Bromfield lautet:

Fast jede Sprache mit einem statischen Typsystem hat auch ein dynamisches Typsystem. Abgesehen von C fällt mir keine Ausnahme ein

Ist das eine berechtigte Behauptung? Ich verstehe, dass Java mit Reflection- oder Loading-Klassen zur Laufzeit ein bisschen wie folgt wird - aber kann diese Idee des "schrittweisen Tippens" auf eine große Anzahl von Sprachen ausgedehnt werden?

Falkenauge
quelle
Ich bin kein Sprachexperte, aber ein paar , die kommen , um sofort etwas dagegen , dass ich glaube , habe keine dynamischen Typen (Nativ, nicht sagen , man konnte nicht Cobble etwas zusammen in diesen) - Fortran, Ada, Pascal, Cobol
mattnz
4
Ich bin ein Sprachexperte und ich bin nicht sicher, was dieser Typ behauptet. "statisch" und "dynamisch" sind falsche Bezeichnungen und geben normalerweise den Zeitpunkt der Typprüfung / -analyse an (ob zur Kompilierungszeit oder zur Laufzeit). Einige Sprachen vereinbaren beides (z. B. indem sie Operationen auf nicht unterstützten Typen während der Kompilierung nicht zulassen und während der Laufzeit Ausnahmen wie ClassCastException auslösen), und das ist möglicherweise das, was er meint. In diesem Fall führt C normalerweise keine Typprüfung zur Laufzeit durch. Eine Sprache sagen. hat kein "dynamisches Typensystem" macht keinen Sinn.
Thiago Silva

Antworten:

37

Original Hochtöner hier. :)

Zunächst bin ich etwas amüsiert / schockiert, dass mein Tweet so ernst genommen wird! Wenn ich gewusst hätte, dass es so weit verbreitet wird, hätte ich mehr als 30 Sekunden damit verbracht, es zu schreiben!

Thiago Silva weist zutreffend darauf hin, dass "statisch" und "dynamisch" die Typprüfung genauer beschreiben als Schriftsysteme . Tatsächlich ist es nicht richtig zu sagen, dass eine Sprache entweder statisch oder dynamisch typisiert ist. Vielmehr hat eine Sprache ein Typensystem, und eine Implementierung dieser Sprache erzwingt das Typensystem möglicherweise durch statische Prüfung oder dynamische Prüfung oder beides oder beides (obwohl dies keine sehr ansprechende Sprachimplementierung wäre!).

Es gibt bestimmte Typsysteme (oder Merkmale von Typsystemen), die für statische Überprüfungen besser geeignet sind, und bestimmte Typsysteme (oder Merkmale von Typsystemen), die für dynamische Überprüfungen besser geeignet sind. Wenn Sie beispielsweise in Ihrer Sprache im Text eines Programms angeben können, dass ein bestimmter Wert immer ein Array von Ganzzahlen sein muss, ist es relativ einfach, ein statisches Prüfprogramm zu schreiben, um diese Eigenschaft zu überprüfen. Umgekehrt, wenn Ihre Sprache Untertypen hat und Downcasting zulässt, ist es relativ einfach, die Gültigkeit eines Downcasts zur Laufzeit zu überprüfen, dies ist jedoch zur Kompilierungszeit äußerst schwierig.

Was ich mit meinem Tweet wirklich gemeint habe, ist einfach, dass die überwiegende Mehrheit der Sprachimplementierungen eine gewisse dynamische Typprüfung durchführt. Ebenso hat die überwiegende Mehrheit der Sprachen einige Merkmale, die sich nur schwer (wenn nicht gar unmöglich) statisch überprüfen lassen. Downcasting ist ein Beispiel. Weitere Beispiele sind ein arithmetischer Überlauf, die Überprüfung der Array-Grenzen und die Überprüfung auf Null. Einige davon können unter bestimmten Umständen statisch überprüft werden, aber im Großen und Ganzen ist es schwierig, eine Sprachimplementierung zu finden, die zur Laufzeit keine Überprüfung durchführt.

Das ist keine schlechte Sache. Es ist nur eine Beobachtung, dass es viele interessante Eigenschaften gibt, die unsere Sprachen erzwingen sollen, und dass wir nicht wirklich wissen, wie man sie statisch überprüft. Und es ist eine Erinnerung daran, dass Unterscheidungen wie "statische Typen" gegenüber "dynamischen Typen" bei weitem nicht so eindeutig sind, wie manche Leute glauben machen. :)

Eine letzte Anmerkung: Die Begriffe "stark" und "schwach" werden in der Programmiersprachenforschung nicht wirklich verwendet und haben auch keine wirklich einheitliche Bedeutung. Im Allgemeinen habe ich festgestellt, dass jemand, der sagt, dass eine Sprache "stark tippt" und eine andere "schwach tippt", wirklich sagt, dass seine Lieblingssprache (die mit "stark tippt") dies verhindert einen Fehler machen, den die andere Sprache (die mit "schwacher Typisierung") nicht macht - oder umgekehrt, dass ihre Lieblingssprache (die mit "schwacher Typisierung") es ihnen erlaubt, eine coole Sache zu machen, die die andere Sprache (die "schwache Typisierung") macht eine mit "starker typisierung") tut das nicht.

Aleks Bromfield
quelle
9
"Es ist nur eine Beobachtung, dass es viele interessante Eigenschaften gibt, die unsere Sprachen erzwingen sollen, und dass wir nicht wirklich wissen, wie man sie statisch überprüft." - Nun, es ist nicht nur so, dass wir nicht wissen, wie es geht. „Ist dieses Programm halt für diesen Eingang“ ist eine interessante Eigenschaft , dass wir nicht nur wissen nicht , wie zu überprüfen, aber in der Tat wir tun weiß , ist unmöglich zu überprüfen.
Jörg W Mittag
"Andere Beispiele sind der arithmetische Überlauf, die Überprüfung der Array-Grenzen und die Überprüfung auf Null." Nur eine Anmerkung: Es gibt zwar einige Sprachen, die diese Eigenschaften im Typsystem prüfen, aber selbst in den meisten dynamisch getippten Sprachen sind diese normalerweise ein Merkmal von Werten und nicht von Typen. ("Null" -Prüfung ist jedoch eine häufige Funktion von Systemen mit statischem Typ.)
porglezomp
Es ist ein dynamischer Typ - System , und ein statischer Typ System . Auch wenn keines von beiden erzwungen wird, sind sie immer noch da und beschreiben, was richtig und was ein Fehler ist. Die Sprache / Implementierung kann eine von beiden prüfen oder nicht. Übrigens sollten das statische und das dynamische Typsystem konsistent sein, aber dies ist per Definition nicht der Fall .
Elazar
Jede Sprache verfügt über ein dynamisches Typsystem, bei dem es sich um eine Reihe von Regeln handelt, die die Ausführung bestimmter Vorgänge verhindern.
Elazar
7

Nun ja. Sie können Eigentumstaschen in jeder statisch typisierten Sprache haben. Die Syntax wird furchtbar sein, während Sie gleichzeitig alle Nachteile eines dynamisch typisierten Systems erfahren. Es gibt also keinen wirklichen Vorteil, es sei denn, der Compiler erlaubt Ihnen, eine schönere Syntax zu verwenden, wie es C # dynamictut.

Das geht auch in C ganz einfach.

Als Reaktion auf andere Antworten: Ich denke, die Leute verwechseln statisches / dynamisches Tippen mit starkem / schwachem Tippen. Bei der dynamischen Typisierung geht es darum, die Datenstruktur zur Laufzeit ändern zu können und Code Daten zu verwenden, die genau den Anforderungen des Codes entsprechen. Dies nennt man Duck Typing .

Das Erwähnen von Reflektion sagt nicht die ganze Geschichte aus, da Sie durch Reflektion die vorhandene Datenstruktur nicht ändern können. Sie können einer Klasse oder Struktur weder in C, C ++, Java noch in C # ein neues Feld hinzufügen. In dynamischen Sprachen ist das Hinzufügen neuer Felder oder Attribute zu vorhandenen Klassen nicht nur möglich, sondern durchaus üblich.

Betrachten Sie beispielsweise Cython , den Python-to-C-Compiler. Es wird statischer C-Code erstellt, das Typsystem behält jedoch seine dynamische Natur bei. C ist eine statisch typisierte Sprache, unterstützt jedoch dynamisches Schreiben in Python.

Euphorisch
quelle
Technisch gesehen können Sie dies in C # ab Version 4 tun ExpandoObject, obwohl dies im Gegensatz zu JavaScript oder Ruby ein Opt-In-Prozess ist. Trotzdem haben Sie einen sehr wichtigen Punkt angesprochen, nämlich, dass das Tippen von Enten (was 99% der Entwickler tatsächlich bedeuten, wenn sie "dynamisch getippt" sagen) und das Nachdenken überhaupt nicht dasselbe sind.
Aaronaught
Könnte ich auch hinzufügen, dass Python keine echte Ente Typisierung hat. Es hat nur ein paar Haken für Sie, um "Ihre eigenen zu implementieren" (hat eine magische Methode, die zurückkehren kann, Trueum zu sagen, "dieses verrückte Objekt ist eine Instanz der Klasse, die ich definiere"). OCaml hat diese Funktion, soweit ich weiß, aber ich weiß es nicht wirklich.
Vlad-Ardelean
6

Dynamische Sprachen sind statische Sprachen . Was gemeinhin als "dynamisches Tippen" bezeichnet wird, ist ein Sonderfall des statischen Tippens - der Fall, in dem Sie sich darauf beschränkt haben, nur einen Typ zu haben. Stellen Sie sich als Gedankenexperiment vor, Sie schreiben ein Programm in Java oder C #, indem Sie nur ObjectVariablen / Felder / Parameter verwenden und unmittelbar vor dem Aufruf einer Methode ein Downcast durchführen. Es wäre genauer, Sprachen wie Python oder Javascript "unityped" zu nennen. (Diese Behauptung wird wahrscheinlich viele Leute verwirren / stören, wenn man bedenkt, dass ein solches Java- oder C # -Programm viele Untertypen verwenden würde, aber das liegt daran, dass die durchschnittliche OOP-Sprache Typen und Klassen zusammenführt. Weitere Informationen finden Sie im Blogbeitrag.)

Beachten Sie, dass selbst C eine "dynamische" Typisierung hat - Sie können jeden Zeiger auf einen Zeiger void(und wenn der Speicher mir dient char) und wieder zurück setzen. Beachten Sie auch, dass dort keine Laufzeitüberprüfung stattfindet. Wenn Sie es falsch verstehen, genießen Sie Ihr undefiniertes Verhalten!

Doval
quelle
Die Besetzung erfolgt jedoch statisch. Ich verstehe nicht, wie dies ein Beispiel für dynamisches Tippen ist.
osa
@osa Nur weil der Code sagt, dass String foo = (String) bares nicht heißt, dass bares sich tatsächlich um eine handelt String. Das kann man nur zur Laufzeit genau wissen, daher sehe ich nicht, wie die Besetzung "statisch" erfolgt.
Doval
Dem stimme ich nicht zu. Zumindest in Javascript können Sie Objekten zur Laufzeit neue Methoden und Eigenschaften hinzufügen. In C # müssen Sie dazu explizit ein dynamicObjekt verwenden. Wenn Sie versuchen, einer object... eine Eigenschaft hinzuzufügen , können Sie das nicht.
Arturo Torres Sánchez
3

Der Unterschied zwischen statischer und dynamischer Typisierung besteht darin, dass der Typ eines Werts überprüft wird: Kompilierungszeit vs. Laufzeit. In Sprachen, in denen Werte ihren Typ haben (z. B. Java-Objekte), können Sie immer auf dynamisches Tippen zurückgreifen, auch wenn die Sprache statisches Tippen bevorzugt. Hier ist ein Beispiel in Java mit einer dynamisch typisierten Methode:

void horribleJava(List untypedList) {
  for (Object o : untypedList)
    ((SpecificType) o).frobnicate();
}

Beachten Sie, wie der Typ jedes Elements zur Laufzeit überprüft wird. Die äquivalente statisch typisierte Methode lautet:

void goodJava(List<SpecificType> typedList) {
  for (SpecificType o : typedList) {
    o.forbnicate();
  }
}

In C behalten Werte (und insbesondere Zeiger) ihren Typ während der Laufzeit nicht bei - jeder Zeiger entspricht a void *. Stattdessen haben Variablen und Ausdrücke einen Typ. Um eine dynamische Typisierung zu erreichen, müssen Sie die Typinformationen selbst mitführen (z. B. als Feld in einer Struktur).

amon
quelle
1
Ich denke nicht, dass eine Besetzung in einem wirklich praktischen Sinne als dynamisches Tippen gilt. Es erfordert immer noch Kenntnisse über den Typ zur Kompilierungszeit, es verschiebt nur die tatsächliche Validierung bis zur Laufzeit. Sie können die frobnicateMethode hier nicht aufrufen , ohne vorher darüber Bescheid zu wissen SpecificType.
Aaronaught
2
@Aaronaught Dies ist jedoch eine dynamische Eingabe, sobald die Typprüfung auf die Laufzeit verschoben wurde. Beachten Sie, dass in beiden Beispielen eine nominative Typisierung verwendet wird , für die ein bestimmter Typname erforderlich ist. Sie denken an eine strukturelle Typisierung , z. B. die Eingabe von Enten, für die eine bestimmte Methode erforderlich ist. Die Achsen für strukturelle vs. nominative und statische vs. dynamische Typisierung sind orthogonal zueinander (Java ist nominativ und meist statisch; ML und Go sind strukturell und statisch; Perl, Python und Ruby sind (meistens) strukturell und dynamisch).
amon
Wie ich in meinem letzten Kommentar sagte, ist es eine theoretische Unterscheidung, die keinem Programmierer, den ich jemals getroffen habe, wirklich am Herzen liegt. Man kann mit Schärfe argumentieren, dass Menschen die falsche Terminologie verwenden (und es scheint tatsächlich, dass der ursprüngliche Hochtöner genau diese Art von Snarkiness anstrebte), aber für Menschen, die sich tatsächlich im Graben befinden, ist dynamisches Tippen = Ententippen. Tatsächlich ist diese Definition so verbreitet, dass Sprachen wie C # sie tatsächlich kodiert haben (dh mit dem dynamicSchlüsselwort). Die Gleichsetzung von statischen zu Kompilierung- und dynamisch zur Laufzeit meist Muddies nur das Wasser.
Aaronaught
@Aaronaught: Wenn man auf eine Schnittstelle umwandelt und Klassen, die eine Methode mit der beabsichtigten Bedeutung implementieren, konsistent dieselbe Schnittstelle implementieren, um dies zu sagen, dann wäre man im Wesentlichen ein Laufzeit-Ententyp. Während einige vielleicht argumentieren, dass "duck typing" nur den Methodennamen verwenden sollte, halte ich es für hilfreicher, den "echten" Methodennamen als den Schnittstellennamen plus den Methodennamen zu betrachten. Andernfalls sollte, [1,2].Add([3,4])ergeben [1,2,3,4], [1,2,[3,4]]oder [4,6]?
Supercat
@supercat: Keine der oben genannten AddMethoden sollte fehlschlagen, da es keine Methode im Array gibt, die ein Array akzeptiert, da eine solche Methode mehrdeutig wäre. Duck Typing entschuldigt Sie nicht, verständliche Typen und Funktionen zu schreiben.
Aaronaught
2

Statische oder dynamische Typisierung bezieht sich im Wesentlichen darauf, wie Typen überprüft werden. Statische Typisierung bedeutet, dass die Überprüfung der Typen verschiedener Variablen oder Ausdrücke basierend auf dem tatsächlichen Code (normalerweise vom Compiler) überprüft wird, während in einem dynamischen Typensystem diese Überprüfung nur zur Laufzeit von der Laufzeitumgebung durchgeführt wird.

Ich glaube, der Text bezieht sich darauf, dass auch wenn die Typen tatsächlich statisch überprüft werden, sie auch zur Laufzeit, dh dynamisch, überprüft werden. Sie haben Java Reflection richtig erwähnt. Reflexion findet nur zur Laufzeit statt, und die Java Runtime Environment (JVM) führt bei Verwendung von Reflexion tatsächlich eine Typprüfung durch, was im Grunde genommen eine dynamische Typprüfung bedeutet.

m3th0dman
quelle
Dann ist es nicht richtig. In C ++, Pascal und Fortran werden die Typen in allen kompilierten Sprachen nur statisch und nicht dynamisch überprüft (es sei denn, Sie verwenden dynamic_cast). Sie können Zeiger verwenden, um beliebige Daten in den Speicher zu schreiben, und niemand kennt die Typen. Statische Eingabe bedeutet also NUR STATISCH ÜBERPRÜFEN, während dynamische Eingabe NUR ÜBERPRÜFEN bedeutet.
osa
-1

Die Erklärung ist falsch: C hat auch ein wichtiges dynamisches Typensystem. Es überprüft es einfach nicht ("C ist stark typisiert, schwach überprüft"). Wenn Sie beispielsweise eine Struktur als double( reinternpret_cast-Stil) behandeln, entsteht ein undefiniertes Verhalten - ein dynamischer Typfehler.

Elazar
quelle
(Zu wem auch immer downvoted - @Thiago Silva Kommentar sagt über die gleiche Sache)
Elazar