Ausdrücke wie "statisches Tippen" und "dynamisches Tippen" werden häufig verwendet, und die Leute tendieren dazu, subtil unterschiedliche Definitionen zu verwenden. Beginnen wir also damit, zu klären, was wir meinen.
Stellen Sie sich eine Sprache mit statischen Typen vor, die zur Kompilierungszeit überprüft werden. Nehmen wir jedoch an, dass ein Typfehler nur eine nicht schwerwiegende Warnung generiert und dass zur Laufzeit alles vom Typ "Ente" ist. Diese statischen Typen dienen nur dem Komfort des Programmierers und wirken sich nicht auf das Codegen aus. Dies zeigt, dass die statische Typisierung für sich genommen keine Einschränkungen mit sich bringt und sich bei der dynamischen Typisierung nicht gegenseitig ausschließt. (Objective-C ist sehr ähnlich.)
Die meisten statischen Systeme verhalten sich jedoch nicht so. Es gibt zwei allgemeine Eigenschaften von statischen Typsystemen, die Einschränkungen auferlegen können:
Der Compiler kann ein Programm ablehnen, das einen statischen Typfehler enthält.
Dies ist eine Einschränkung, da viele typsichere Programme notwendigerweise einen statischen Typfehler enthalten.
Ich habe beispielsweise ein Python-Skript, das sowohl als Python 2 als auch als Python 3 ausgeführt werden muss. Einige Funktionen haben ihre Parametertypen zwischen Python 2 und 3 geändert, sodass ich folgenden Code habe:
if sys.version_info[0] == 2:
wfile.write(txt)
else:
wfile.write(bytes(txt, 'utf-8'))
Eine statische Python 2-Typprüfung würde den Python 3-Code (und umgekehrt) ablehnen, obwohl er niemals ausgeführt würde. Mein typsicheres Programm enthält einen statischen Typfehler.
Ein weiteres Beispiel ist ein Mac-Programm, das unter OS X 10.6 ausgeführt werden soll, jedoch die neuen Funktionen in 10.7 nutzt. Die 10.7-Methoden können zur Laufzeit vorhanden sein oder auch nicht, und es liegt an mir, dem Programmierer, sie zu erkennen. Eine statische Typprüfung muss entweder mein Programm ablehnen, um die Typensicherheit zu gewährleisten, oder das Programm akzeptieren, zusammen mit der Möglichkeit, zur Laufzeit einen Typfehler (fehlende Funktion) zu erzeugen.
Bei der statischen Typprüfung wird davon ausgegangen, dass die Laufzeitumgebung durch die Informationen zur Kompilierungszeit angemessen beschrieben wird. Aber die Zukunft vorherzusagen ist gefährlich!
Hier ist noch eine Einschränkung:
Der Compiler generiert möglicherweise Code, der davon ausgeht, dass der Laufzeittyp der statische Typ ist.
Unter der Annahme, dass die statischen Typen "korrekt" sind, ergeben sich viele Optimierungsmöglichkeiten, die jedoch einschränkend sein können. Ein gutes Beispiel sind Proxy-Objekte, zB Remoting. Angenommen, Sie möchten ein lokales Proxy-Objekt haben, das Methodenaufrufe an ein reales Objekt in einem anderen Prozess weiterleitet. Es wäre schön, wenn der Proxy generisch wäre (so dass er sich als jedes Objekt tarnen kann) und transparent (so dass der vorhandene Code nicht wissen muss, dass er mit einem Proxy kommuniziert). Dazu kann der Compiler jedoch keinen Code generieren, der davon ausgeht, dass die statischen Typen korrekt sind, z. B. durch statische Inlining-Methodenaufrufe, da dies fehlschlägt, wenn das Objekt tatsächlich ein Proxy ist.
Beispiele für ein solches Remoting in Aktion sind NSXPCConnection von ObjC oder TransparentProxy von C # (für dessen Implementierung einige Pessimierungen in der Laufzeit erforderlich waren - siehe hier für eine Diskussion).
Wenn der Codegen nicht von den statischen Typen abhängig ist und Sie über Funktionen wie die Nachrichtenweiterleitung verfügen, können Sie mit Proxy-Objekten, dem Debuggen usw. viele interessante Aufgaben ausführen.
Das ist also eine Auswahl von Dingen, die Sie tun können, wenn Sie keinen Typprüfer benötigen. Die Einschränkungen werden nicht durch statische Typen auferlegt, sondern durch erzwungene statische Typprüfung.