OCaml Konzepte: Grundlegende Konzepte - Polymorphismus |
Polymorphie ("Vielgestaltigkeit") ist ein wichtiges Konzept objektorientierter und funktionaler Sprachen.
Die unterschiedlichen Erscheinungsformen haben als gemeinsamen Nenner, daß allgemein ein Ausdruck für mehrere,
unterschiedliche Typen definiert ist.
Polymorphismus | In OO-Sprachen:
|
|
parametrischer Polymorphismus | In ML-Sprachen:
|
|
Typvariable | Platzhalter für beliebige Typen | |
Let-bound Polymorphismus |
|
Man unterscheidet im Allgemeinen zwischen parametrischer und Ad-hoc-Polymorphie.
Parametrische Polymorphie liegt vor, wenn Funktionen gleichartig auf einer ganzen Klasse von Datenobjekten wirken.
Typisch für die Ad-hoc-Polymorphie objektorientierter Sprachen sind überladene Operatoren wie z.B. die arithmetischen Funktionen, die für Ganzzahlen
und Gleitkommazahlen definiert sind, oder die Gleichheitsfunktion, die für unterschiedliche
Typen unterschiedlich definiert werden muss, aber dennoch eine feste Semantik
besitzt. Hier steht dasselbe Symbol für eine Reihe von unterschiedlichen Funktionen, von
denen je nach Kontext die passende ausgewählt wird.
Das Hindley/Milner-Typsystem unterstützt nur die parametrische Polymorphie, die in der ML-Sprachfamilie durch die
Let-Bindung und Restriktionen, welche sich aus den nachteiligen Effekten der imperativen Sprachkonstrukte ergeben,
eingeschränkt wird.
Die Interaktion zwischen Polymorphismus und Seiteneffekten der imperativen Konstrukte in der ML-Sprachfamilie stellt allerdings seit jeher ein ärgerliches Problem dar.
Beispiel: Polymorphe Funktion |
# let identity x = x;; val identity : 'a -> 'a = <fun> |
|
# identity 1;; - : int = 1 |
||
# identity "Hello";; - : string = "Hello" |
Die inferierte Typvariable 'a bezeichnet einen sogenannten Typparameter und lässt die Funktion identity offen
für beliebige Datentypen.
Die Funktion identity erwartet also einen Wert beliebigen Typs und liefert genau diesen zurück.
Generell sind Funktionen in OCaml, welche lediglich Werte umkopieren (Komposition und Dekomposition von eigenen Datentypen) oder
relationale Operatoren verwenden polymorph, also für beliebige Typen anwendbar.
Polymorphismus ist beschränkt auf Werte ! | ||
Beispiel: Partielle Applikation |
# let identity' = identity identity;; val identity : '_a -> '_a = <fun> |
|
# identity' 1;; - : int = 1 |
||
# identity';; - : int -> int = <fun> |
Die Typvariable '_a ist kein Typparameter, sondern ein unbekannter Typ, welcher auf eine Wertzuweisung (Instanzierung) wartet.
Danach ist der Typ der Variablen '_a auf den Typen des zugewiesenen Wertes permanent festgelegt.
Eine Lösung für diese Problematik ist die Eta-Expansion, d.h. die explizite Angabe von Argumenten.
Werte |
|
|
Keine Werte |
|
Lösung durch explizite Angabe von Argumenten ! | ||
Beispiel: Eta-Expansion |
# let identity' x = ( identity identity) x;; val identity' : 'a -> 'a = <fun> |
Durch die explizite Angabe des Parameters x und entsprechender Klammerung erhält man wieder eine polymorphe Form der Funktion.
Beispiel: Overloading in Java |
|
Der Plus-Operator (+) ist beispielsweise in Java überladen für Ganzzahlen, Fließkommazahlen, sogar für Zeichenketten, wo er die Konkatenation repräsentiert. Jedoch fordert der Ad-hoc Polymorphismus eine eindeutige Angabe (hier durch die explizite Festlegung der Parametertypen) zur Auswahl der entsprechenden Methode und Kompilierung des/der entsprechenden Maschinenbefehle zur Berechnung. Im Gegensatz zu Java unterstützt OCaml kein Überladen von Funktionen und Methoden.
Annahme: | Plus-Operator (+) überladen für int und float | |
# let add x y = x + y;; |
||
Typ der Funktion ? |
val add : int -> int -> int oderval add : float -> float -> float |
OCaml unterstützt kein Überladen von Methoden und Funktionen, da für den Mechanismus der Typinferenz unentscheidbare Situationen (vor allem auch durch Seiteneffekte) nicht ausgeschlossen werden können.
Hinweis:
"#" kennzeichnet Eingaben im OCaml Interpreter,
"val" bzw "- :" das Ergebnis der Auswertung und automatischen Typanalyse inkl. der vom System inferierten Typen.