Es gibt einige wichtige Punkte, die meiner Meinung nach alle vorhandenen Antworten übersehen haben.
Schwache Eingabe bedeutet, den Zugriff auf die zugrunde liegende Darstellung zu ermöglichen. In C kann ich einen Zeiger auf Zeichen erstellen und dann dem Compiler mitteilen, dass ich ihn als Zeiger auf Ganzzahlen verwenden möchte:
char sz[] = "abcdefg";
int *i = (int *)sz;
Auf einer Little-Endian-Plattform mit 32-Bit-Ganzzahlen wird daraus i
ein Array der Zahlen 0x64636261
und 0x00676665
. Sie können sogar selbst Zeiger auf Ganzzahlen (der entsprechenden Größe) umwandeln:
intptr_t i = (intptr_t)&sz;
Und das bedeutet natürlich, dass ich Speicher überall im System überschreiben kann. *
char *spam = (char *)0x12345678
spam[0] = 0;
* Natürlich verwenden moderne Betriebssysteme virtuellen Speicher und Seitenschutz, sodass ich nur den Speicher meines eigenen Prozesses überschreiben kann, aber nichts an C selbst bietet einen solchen Schutz, wie jeder, der jemals beispielsweise auf Classic Mac OS oder Win16 codiert hat, Ihnen sagen kann.
Traditionelles Lisp erlaubte ähnliche Arten von Hackerangriffen; Auf einigen Plattformen waren Doppelwort-Floats und Cons-Zellen vom gleichen Typ, und Sie konnten einfach eine an eine Funktion übergeben, die die andere erwartet, und es würde "funktionieren".
Die meisten Sprachen sind heute nicht ganz so schwach wie C und Lisp, aber viele von ihnen sind immer noch etwas undicht. Zum Beispiel jede OO-Sprache mit einem ungeprüften "Downcast" *, das ein Typleck ist: Sie sagen dem Compiler im Wesentlichen: "Ich weiß, ich habe Ihnen nicht genügend Informationen gegeben, um zu wissen, dass dies sicher ist, aber ich bin mir ziemlich sicher es ist, "wenn der springende Punkt eines Typsystems ist, dass der Compiler immer genug Informationen hat, um zu wissen, was sicher ist.
* Ein überprüfter Downcast macht das Typensystem der Sprache nicht schwächer, nur weil die Prüfung auf Laufzeit verschoben wird. Wenn dies der Fall wäre, wäre der Subtyp-Polymorphismus (auch bekannt als virtuelle oder volldynamische Funktionsaufrufe) dieselbe Verletzung des Typsystems, und ich glaube, niemand möchte das sagen.
Sehr wenige "Skriptsprachen" sind in diesem Sinne schwach. Selbst in Perl oder Tcl können Sie einen String nicht nehmen und seine Bytes nur als Ganzzahl interpretieren. * Aber es ist erwähnenswert, dass Sie in CPython (und ähnlich für viele andere Interpreter für viele Sprachen) wirklich hartnäckig sind kann verwendet ctypes
werden libpython
, um ein Objekt zu laden , auf ein Objekt id
zu werfen POINTER(Py_Object)
und das Typsystem zum Auslaufen zu zwingen. Ob dies das Typsystem schwach macht oder nicht, hängt von Ihren Anwendungsfällen ab. Wenn Sie versuchen, eine Sandbox mit eingeschränkter Ausführung in der Sprache zu implementieren, um die Sicherheit zu gewährleisten, müssen Sie sich mit solchen Escape-Effekten auseinandersetzen.
* Sie können eine Funktion wie verwenden struct.unpack
, um die Bytes zu lesen und ein neues int aus "wie C diese Bytes darstellen würde" zu erstellen, aber das ist offensichtlich nicht undicht; sogar Haskell erlaubt das.
In der Zwischenzeit unterscheidet sich die implizite Konvertierung wirklich von einem schwachen oder undichten System.
Jede Sprache, auch Haskell, hat Funktionen, um beispielsweise eine Ganzzahl in einen String oder ein Float umzuwandeln. Einige Sprachen führen einige dieser Konvertierungen jedoch automatisch für Sie durch, z. B. in C, wenn Sie eine Funktion aufrufen, die eine möchte float
, und diese übergebenint
, wird sie für Sie konvertiert. Dies kann definitiv zu Fehlern mit beispielsweise unerwarteten Überläufen führen, aber es handelt sich nicht um die gleichen Arten von Fehlern, die Sie von einem schwachen Typsystem erhalten. Und C ist hier nicht wirklich schwächer; Sie können in Haskell ein int und ein float hinzufügen oder sogar ein float zu einer Zeichenfolge verketten. Sie müssen dies nur expliziter tun.
Und mit dynamischen Sprachen ist das ziemlich trübe. In Python oder Perl gibt es keine "Funktion, die einen Float will". Es gibt jedoch überladene Funktionen, die unterschiedliche Aufgaben mit unterschiedlichen Typen ausführen, und es besteht ein starkes intuitives Gefühl, dass das Hinzufügen einer Zeichenfolge zu etwas anderem "eine Funktion ist, die eine Zeichenfolge möchte". In diesem Sinne scheinen Perl, Tcl und JavaScript viele implizite Konvertierungen durchzuführen ( "a" + 1
gibt Ihnen "a1"
), während Python viel weniger ausführt ( "a" + 1
eine Ausnahme 1.0 + 1
auslöst, Ihnen aber 2.0
* gibt). Es ist nur schwer, diesen Sinn in formale Begriffe zu fassen - warum sollte es keine geben +
, die einen String und ein Int benötigt, wenn es offensichtlich andere Funktionen wie die Indizierung gibt, die dies tun?
* Tatsächlich kann dies in modernem Python durch OO-Subtypisierung erklärt werden, da dies isinstance(2, numbers.Real)
wahr ist. Ich glaube nicht, dass es in irgendeiner Weise 2
eine Instanz des Stringtyps in Perl oder JavaScript gibt ... obwohl dies in Tcl tatsächlich der Fall ist, da alles eine Instanz eines Strings ist.
Schließlich gibt es noch eine andere, vollständig orthogonale Definition von "stark" vs. "schwach", wobei "stark" kraftvoll / flexibel / ausdrucksstark bedeutet.
Mit Haskell können Sie beispielsweise einen Typ definieren, der eine Zahl, eine Zeichenfolge, eine Liste dieses Typs oder eine Zuordnung von Zeichenfolgen zu diesem Typ ist. Dies ist eine perfekte Möglichkeit, alles darzustellen, was aus JSON dekodiert werden kann. Es gibt keine Möglichkeit, einen solchen Typ in Java zu definieren. Aber zumindest hat Java parametrische (generische) Typen, sodass Sie eine Funktion schreiben können, die eine Liste von T verwendet und weiß, dass die Elemente vom Typ T sind. Andere Sprachen, wie das frühe Java, haben Sie gezwungen, eine Liste von Objekten und Downcasts zu verwenden. Zumindest mit Java können Sie jedoch neue Typen mit eigenen Methoden erstellen. Mit C können Sie nur Strukturen erstellen. Und BCPL hatte das nicht einmal. Und so weiter bis zur Montage, wo die einzigen Typen unterschiedliche Bitlängen sind.
In diesem Sinne ist das Haskell-Typsystem stärker als das moderne Java, das stärker ist als das frühere Java, das stärker ist als das C, das stärker als das BCPL ist.
Wo passt Python in dieses Spektrum? Das ist ein bisschen schwierig. In vielen Fällen können Sie mit der Eingabe von Enten alles simulieren, was Sie in Haskell tun können, und sogar einige Dinge, die Sie nicht können. Sicher, Fehler werden zur Laufzeit anstatt zur Kompilierungszeit abgefangen, aber sie werden immer noch abgefangen. Es gibt jedoch Fälle, in denen das Tippen von Enten nicht ausreicht. In Haskell können Sie beispielsweise feststellen, dass eine leere Liste von Ints eine Liste von Ints ist, sodass Sie entscheiden können, dass das Reduzieren +
über diese Liste 0 * zurückgeben soll. In Python ist eine leere Liste eine leere Liste. Es gibt keine Typinformationen, die Ihnen bei der Entscheidung helfen, was eine Reduzierung +
bewirken soll.
* Tatsächlich lässt Haskell Sie dies nicht zu. Wenn Sie die Reduktionsfunktion aufrufen, die keinen Startwert für eine leere Liste annimmt, wird eine Fehlermeldung angezeigt. Aber seine Art System ist leistungsfähig genug , dass man könnte diese Arbeit machen, und Python ist es nicht.