:Typinferenz geht auch bei dynamischer Typisierung. Im RefactoringBrowser für SpracheSmalltalk ist ein Typinferenzer integriert. Der ist allerdings etwas versteckt. Er erzeugt einen Klassen-Kommentar, in dem für jede Membervariable alle möglichen Typen angegeben werden. Der Inferenzer leistet allerdings erheblich weniger, als bei statsicher Typisierung und insbesondere bei funktionaler Programmierung möglich wäre. Typannotationen im Stile dessen, was der RefactoringBrowser vorschlägt, werden in Smalltalk benötigt, um das Rountrip-Engeneering per Case-Tool zu vereinfachen. |
Hindley-Milner-Typsystem | ![]() |
Hindley und Milner entwickelten dieses Typsystem, Erweiterungen davon bilden die Basis von Standard ML, Haskell und einigen anderen funktionalen Programmiersprachen. Das besondere am Hindley-Milner-Typsystem ist, dass jedes Stück Code genau einen allgemeinsten Typ besitzt, und dass es einen Algorithmus, eben die HindleyMilnerTypInferenz, gibt, der diesen allgemeinsten Typ findet. Typen sind:
Typinferenz | ![]() |
Die Inferenz (ist Typabgleich eine gute Übersetzung?) selbst ist auch recht einfach. Alle Variablen im Programm erhalten zunächst den Typ "a" (a ist eine noch unbenutzte Typvariable), alle Funktionen haben bereits einen, ebenso Konstanten und Datenkonstruktoren. Jetzt werden Typregeln angewandt, die dazu führen, dass bestimmte Typen gleich sein müssen. Tatsächlich sind es nur zwei Regeln: eine Funktion muss auf ihren Argumenttyp angewandt werden und beide gemeinsam haben den Ergebnistyp und eine weitere Regel für rekursive Aufrufe. Sollen zwei Typen gleich sein, werden sie gleichgesetzt, damit werden die Variablen an Typen gebunden. Klappt das Gleichsetzen (unification), sind am Ende die Variablen an den allgemeinsten Typ gebunden. Klappt sie nicht, gibt es gar keinen Typ, das Programm ist falsch.
In Haskell gibt es außerdem noch Typklassen. Normalerweise passt eine Typvariable auf jeden Typ. Sie kann aber eingeschränkt werden, so dass sie nur noch auf einen Typ der entsprechenden Klasse passt. Typen gelangen in eine Klasse, indem man deklariert, dass der Typ zu der Klasse gehört und die erforderlichen Methoden implementiert.
Beispiele | ![]() |
Im praktischen Einsatz: Das Hindley-Milner-Typsystem besitzt Typkonstruktoren mit Parametern. So ist etwa eine Liste ein polymorpher Typ:
![]() |
|
zu lesen als "für beliebige Typen a ist eine Liste von Typ-a-Werten entweder leer (Nil) oder die Verkettung (Cons wie construct) eines Elementes vom Typ a mit einer Liste über den Typ a". Man kann es auch so lesen: Nil ist das Listenende, Cons sagt dass noch ein Element kommt und es danach weiter geht. Funktionen können demzufolge auch polymorph sein:
![]() |
|
"für jeden Typ a ist length eine Funktion, die Listen über den Typ a auf ganze Zahlen abbildet". Das geht auch mit mehreren Parametern. In Haskell kann man außerdem die Polymorphie durch Typklassen einschränken, etwa so, dass nur Typen erlaubt sind, die Zahlen darstellen, und die man folgerichtig addieren kann. Das entscheidende ist jetzt, dass jeder Ausdruck in diesem System einen "allgemeinsten Typ" besitzt, der automatisch ermittelt werden kann. Genau dazu muß ein Haskell-Compiler in der Lage sein. Deshalb sind Typdeklarationen in der Regel redundant, mehr Typdeklarationen führen aber oft zu genaueren Fehlermeldungen. Haskell kennt übrigens keinen Type-Cast, und ich hab ihn auch noch nie vermißt.
![]() |
|
![]() |
|
![]() |
|
![]() |
|
![]() |
|
![]() |
|
Wäre so etwas in einer Sprache wie Smalltalk oder Java denkbar? | ![]() |
Auf Smalltalk passt sowas meiner Ansicht nach nicht. Smalltalk lebt davon, dass man Nachrichten "blind" an Objekte sendet, ohne Rücksicht auf deren Typ oder Klasse. Man kann ja sogar Listen von miteinander überhaupt nicht verwandten Objekten erstellen und an jedes davon die gleiche Nachricht senden, manche der Objekte verstehen die vielleicht überhaupt nicht, usw. Das ist alles nicht mit statischer Typisierung zu erfassen. Ja, vielleicht geht es, aber das Resultat wäre nicht mehr Smalltalk. Und Java... Java hat das Objektmodell von Smalltalk übernommen und dann das armselige Typsystem von C (ohne ++) übergestülpt. In Java ist man immer geneigt, alle Interfaces nur mit "Object" zu deklarieren und sieht sich dann gezwungen, beim Methodenaufruf zu casten. Ehrlich gesagt denke ich, Java ist sowieso nicht mehr zu retten. Wer mitdenkt, nimmt (je nach Geschmack und Problemstellung) Smalltalk, C++ oder ML.
Heterogene Container | ![]() |
Was ist der Typ für eine Hashtabelle (Elemente implementieren Hashfunktion)?
Meinst du eine heterogene Tabelle verschiedener Dinge, die eine Hashfunktion besitzen, sonst aber nichts gemeinsam haben?
Falls ja: Das geht nicht. Im Hindley-Milner-System gibt es nur homogene Container. Normalerweise macht das nichts: Irgendwann hole ich ja die Dinge wieder aus dem Container heraus, und dann will ich etwas mit ihnen machen, und zwar nicht zu irgendwas casten! Was immer dann möglich ist, kann ich auch vorher tun und Funktionen in den Container stecken. Dann ist er wieder homogen.
Wenn man eine bestimmte Auswahl an Typen im Blick hat, kann man allerdings einen Typen definieren, der diese Typen vereint. Das ist wie ein typsicheres union in der SpracheCee oder variante Datenverbünde in der SpracheModula2?. Man kann etwa mit
![]() |
|