Automatische Verallgemeinerung (F#)
In F# werden die Typen von Funktionen und Ausdrücken mithilfe von Typrückschluss ausgewertet.In diesem Thema wird beschrieben, wie in F# die Argumente und Typen von Funktionen automatisch verallgemeinert werden, damit sie, sofern möglich, mit mehreren Typen verwendet werden können.
Automatische Verallgemeinerung
Wenn der F#-Compiler Typrückschluss für eine Funktion ausführt, wird bestimmt, ob ein angegebener Parameter ein generischer Parameter sein kann.Der Compiler untersucht jeden Parameter und bestimmt, ob die Funktion eine Abhängigkeit vom angegebenen Typ des Parameters aufweist.Wenn dies nicht der Fall ist, wird der Typ als generischer Typ abgeleitet.
Das folgende Codebeispiel veranschaulicht eine Funktion, die vom Compiler als generische Funktion abgeleitet wird.
let max a b = if a > b then a else b
Der Typ wird als 'a -> 'a -> 'a abgeleitet.
Der Typ gibt an, dass die Funktion zwei Argumente vom gleichen unbekannten Typ akzeptiert und einen Wert von diesem Typ zurückgibt.Die vorherige Funktion kann u. a. darum generisch sein, weil auch der Größer-als-Operator (>) generisch ist.Die Signatur des Größer-als-Operators lautet 'a -> 'a -> bool.Nicht alle Operatoren sind generisch, und wenn im Code einer Funktion ein Parametertyp zusammen mit einer nicht generischen Funktion oder einem nicht generischen Operator verwendet wird, kann dieser Parametertyp nicht verallgemeinert werden.
Da max generisch ist, kann diese Funktion, wie im folgenden Beispiel gezeigt mit den int-, float-Typen usw. verwendet werden.
let biggestFloat = max 2.0 3.0
let biggestInt = max 2 3
Die beiden Argumente müssen jedoch vom gleichen Typ sein.Die Signatur lautet 'a -> 'a -> 'a, nicht 'a -> 'b -> 'a.Aus diesem Grund wird im folgenden Code ein Fehler erzeugt, da die Typen nicht übereinstimmen.
// Error: type mismatch.
let biggestIntFloat = max 2.0 3
Die max-Funktion kann auch mit allen Typen verwendet werden, die den Größer-als-Operator unterstützen.Daher können Sie sie auch für eine Zeichenfolge verwenden, wie im folgenden Code gezeigt.
let testString = max "cab" "cat"
Wertbeschränkung
Der Compiler führt die automatische Verallgemeinerung nur für vollständige Funktionsdefinitionen mit expliziten Argumenten und für einfache unveränderliche Werte aus.
Daher gibt der Compiler einen Fehler aus, wenn Sie Code zu kompilieren versuchen, der weder verallgemeinerbar noch ausreichend durch einen bestimmten Typ eingeschränkt ist.In der Fehlermeldung für dieses Problem wird diese Einschränkung der automatischen Verallgemeinerung von Werten als Wertbeschränkung bezeichnet.
In der Regel tritt der Wertbeschränkungsfehler auf, wenn ein Konstrukt generisch sein soll, der Compiler jedoch nicht über ausreichende Informationen zum Verallgemeinern des Konstrukts verfügt, oder wenn Sie in einem nicht generischen Konstrukt erforderliche Informationen versehentlich auslassen.Sie können den Wertbeschränkungsfehler korrigieren, indem Sie mit einem der folgenden Verfahren explizitere Informationen bereitstellen, um den Typrückschluss umfassender einzugrenzen:
Schränken Sie einen Typ als nicht generischen Typ ein, indem Sie einem Wert oder Parameter eine explizite Typanmerkung hinzufügen.
Wenn die Ursache des Problems ist, dass eine generische Funktion mit einem nicht verallgemeinerbaren Konstrukt definiert ist, z. B. mit einer Funktionszusammensetzung oder mit unvollständig angewendeten Argumenten einer Curry-Funktion, schreiben Sie die Funktion als gewöhnliche Funktionsdefinition neu.
Wenn das Problem durch einen Ausdruck verursacht wird, der zu komplex ist, um verallgemeinert zu werden, ändern Sie den Ausdruck in eine Funktion, indem Sie einen zusätzlichen, nicht verwendeten Parameter hinzufügen.
Fügen Sie explizite generische Typparameter hinzu.Diese Option wird selten verwendet.
In den folgenden Codebeispielen wird jedes dieser Szenarien veranschaulicht.
Fall 1: Ein zu komplexer Ausdruck.In diesem Beispiel sollte es sich bei der Liste counter um ein int option ref handeln, sie ist jedoch nicht als einfacher unveränderlicher Wert definiert.
let counter = ref None
// Adding a type annotation fixes the problem:
let counter : int option ref = ref None
Fall 2: Verwenden eines nicht verallgemeinerbaren Konstrukts zur Definition einer generischen Funktion.In diesem Beispiel ist das Konstrukt nicht verallgemeinerbar, da es die partielle Anwendung von Funktionsargumenten beinhaltet.
let maxhash = max << hash
// The following is acceptable because the argument for maxhash is explicit:
let maxhash obj = (max << hash) obj
Fall 3: Hinzufügen eines zusätzlichen, nicht verwendeten Parameters.Da dieser Ausdruck für die Verallgemeinerung nicht einfach genug ist, wird vom Compiler ein Wertbeschränkungsfehler ausgegeben.
let emptyList10 = Array.create 10 []
// Adding an extra (unused) parameter makes it a function, which is generalizable.
let emptyList10 () = Array.create 10 []
Fall 4: Hinzufügen von Typparametern.
let arrayOf10Lists = Array.create 10 []
// Adding a type parameter and type annotation lets you write a generic value.
let arrayOf10Lists<'T> = Array.create 10 ([]:'T list)
Im letzten Fall wird der Wert eine Typfunktion, die verwendet wird, um Werte von vielen unterschiedlichen Typen zu erstellen, z. B. wie folgt aussehen:
let intLists = arrayOf10Lists<int>
let floatLists = arrayOf10Lists<float>