Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
F# verwendet Typausschluss, um die Typen von Funktionen und Ausdrücken auszuwerten. In diesem Thema wird beschrieben, wie F# die Argumente und Funktionstypen automatisch generalisiert, sodass sie mit mehreren Typen arbeiten, wenn dies möglich ist.
Automatische Generalisierung
Der F#-Compiler bestimmt, ob ein bestimmter Parameter generisch sein kann, wenn er die Typferenz für eine Funktion durchführt. Der Compiler untersucht jeden Parameter und bestimmt, ob die Funktion eine Abhängigkeit vom spezifischen Typ dieses Parameters hat. Wenn dies nicht der Fall ist, wird der Typ als generisch abgeleitet.
Das folgende Codebeispiel veranschaulicht eine Funktion, die der Compiler als generisch angibt.
let max a b = if a > b then a else b
Der Typ wird abgeleitet, um es zu sein 'a -> 'a -> 'a.
Der Typ gibt an, dass dies eine Funktion ist, die zwei Argumente desselben unbekannten Typs verwendet und einen Wert desselben Typs zurückgibt. Einer der Gründe, warum die vorherige Funktion generisch sein kann, ist, dass der Operator größer als (>) selbst generisch ist. Der Operator "größer als" weist die Signatur 'a -> 'a -> boolauf. Nicht alle Operatoren sind generisch, und wenn der Code in einer Funktion einen Parametertyp zusammen mit einer nicht generischen Funktion oder einem nicht generischen Operator verwendet, kann dieser Parametertyp nicht generalisiert werden.
Da max es generisch ist, kann es mit Typen wie int, float, usw. verwendet werden, wie in den folgenden Beispielen gezeigt.
let biggestFloat = max 2.0 3.0
let biggestInt = max 2 3
Die beiden Argumente müssen jedoch denselben Typ aufweisen. Die Signatur lautet 'a -> 'a -> 'a, nicht 'a -> 'b -> 'a. Daher erzeugt der folgende Code einen Fehler, da die Typen nicht übereinstimmen.
// Error: type mismatch.
let biggestIntFloat = max 2.0 3
Die max Funktion funktioniert auch mit jedem Typ, der den Operator größer als unterstützt. Daher könnten Sie sie auch für eine Zeichenfolge verwenden, wie im folgenden Code gezeigt.
let testString = max "cab" "cat"
Werteinschränkung
Der Compiler führt automatische Generalisierung nur für vollständige Funktionsdefinitionen mit expliziten Argumenten und für einfache unveränderliche Werte aus.
Dies bedeutet, dass der Compiler einen Fehler ausgibt, wenn Sie versuchen, Code zu kompilieren, der nicht ausreichend auf einen bestimmten Typ beschränkt ist, aber auch nicht generalisierbar ist. Die Fehlermeldung für dieses Problem bezieht sich auf diese Einschränkung der automatischen Generalisierung für Werte als Werteinschränkung.
Normalerweise tritt der Werteinschränkungsfehler auf, wenn ein Konstrukt generisch sein soll, aber der Compiler nicht genügend Informationen enthält, um es zu generalisieren, oder wenn Sie unbeabsichtigt ausreichende Typinformationen in einem nichtgenerischen Konstrukt weglassen. Die Lösung für den Werteinschränkungsfehler besteht darin, explizitere Informationen bereitzustellen, um das Typeinschlussproblem auf eine der folgenden Arten zu beschränken:
Beschränken Sie einen Typ auf nichtgenerisch, indem Sie einem Wert oder Parameter eine explizite Typanmerkung hinzufügen.
Wenn das Problem ein nichtgeneralisierbares Konstrukt verwendet, um eine generische Funktion zu definieren, z. B. eine Funktionskomposition oder unvollständig angewendete geschweifte Funktionsargumente, versuchen Sie, die Funktion als normale Funktionsdefinition umzuschreiben.
Wenn das Problem ein Ausdruck ist, der zu komplex ist, um generalisiert zu werden, machen Sie es 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.
Die folgenden Codebeispiele veranschaulichen jedes dieser Szenarien.
Fall 1: Zu komplex ein Ausdruck. In diesem Beispiel ist die Liste counter vorgesehen int option ref, aber sie ist 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 nichtgeneralisierbaren Konstrukts zum Definieren einer generischen Funktion. In diesem Beispiel ist das Konstrukt nichtgeneralisierbar, da es eine partielle Anwendung von Funktionsargumenten umfasst.
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 nicht einfach genug für die Generalisierung ist, gibt der Compiler den Wertbeschränkungsfehler aus.
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 zu einer Typfunktion, die zum Erstellen von Werten vieler verschiedener Typen verwendet werden kann, z. B. wie folgt:
let intLists = arrayOf10Lists<int>
let floatLists = arrayOf10Lists<float>