Partager via


Généralisation automatique (F#)

F# utilise l'inférence de type pour évaluer les types de fonctions et d'expressions. Cette rubrique décrit comment F# généralise automatiquement les arguments et les types de fonctions afin qu'ils puissent être utilisés avec plusieurs types lorsque cela est possible.

Généralisation automatique

Lorsque le compilateur F# exécute l'inférence de type sur une fonction, il détermine si un paramètre donné peut être générique. Il examine chaque paramètre et détermine si la fonction a une dépendance sur le type spécifique de ce paramètre. Si ce n'est pas le cas, le compilateur déduit que le type est générique.

Dans l'exemple de code suivant, le compilateur déduit que la fonction est générique.

let max a b = if a > b then a else b

Le type déduit est 'a -> 'a -> 'a.

Le type indique qu'il s'agit d'une fonction qui accepte deux arguments du même type inconnu et qu'elle retourne une valeur de ce même type. L'une des raisons qui explique que la fonction précédente peut être générique est que l'opérateur supérieur à (>) est lui-même générique. L'opérateur supérieur à possède la signature 'a -> 'a -> bool. Tous les opérateurs ne sont pas génériques, et si le code dans une fonction utilise un type de paramètre avec une fonction ou un opérateur non générique, ce type de paramètre ne peut pas être généralisé.

Étant donné que max est générique, il peut être utilisé avec des types tels qu'int, float et ainsi de suite, comme illustré dans les exemples suivants.

let biggestFloat = max 2.0 3.0
let biggestInt = max 2 3

Toutefois, les deux arguments doivent être du même type. La signature est 'a -> 'a -> 'a, et non 'a -> 'b -> 'a. Par conséquent, le code suivant produit une erreur, car les types ne correspondent pas.

// Error: type mismatch.
let biggestIntFloat = max 2.0 3

La fonction max fonctionne également avec tout type qui prend en charge l'opérateur supérieur à. Par conséquent, vous pouvez aussi l'utiliser sur une chaîne, comme indiqué dans le code suivant.

let testString = max "cab" "cat"

Restriction de valeur

Le compilateur exécute la généralisation automatique uniquement sur des définitions de fonction complètes qui ont des arguments explicites, et sur des valeurs immuables simples.

Cela signifie que le compilateur émet une erreur si vous essayez de compiler du code qui n'est pas contraint à être un type spécifique et qui n'est pas généralisable. Le message d'erreur de ce problème appelle restriction de valeur cette restriction sur la généralisation automatique pour les valeurs.

En général, l'erreur de restriction de valeur se produit dans l'un des deux cas suivants : vous voulez qu'une construction soit générique, mais le compilateur a des informations insuffisantes pour la généraliser, ou vous omettez involontairement des informations de type suffisantes dans une construction non générique. La solution à cette erreur de restriction de valeur consiste à fournir des informations plus explicites de manière à mieux contraindre le problème d'inférence de type, de l'une des façons suivantes :

  • Contraignez un type à ne pas être générique en ajoutant une annotation de type explicite à une valeur ou un paramètre.

  • Si le problème utilise une construction non généralisable pour définir une fonction générique, telle qu'une composition de fonction ou des arguments de fonction curryfiée appliqués incomplètement, essayez de réécrire la fonction comme une définition de fonction ordinaire.

  • Si le problème concerne une expression qui est trop complexe pour être généralisée, faites-en une fonction en lui ajoutant un paramètre supplémentaire inutilisé.

  • Ajoutez des paramètres de type générique explicites. Cette option est rarement utilisée.

  • Les exemples de code suivants illustrent chacun de ces scénarios.

Cas 1 : expression trop complexe. Dans cet exemple, la liste counter est conçue pour être int option ref, mais elle n'est pas définie comme une valeur immuable simple.

let counter = ref None
// Adding a type annotation fixes the problem:
let counter : int option ref = ref None

Cas 2 : utilisation d'une construction non généralisable pour définir une fonction générique. Dans cet exemple, la construction est non généralisable parce qu'elle implique l'application partielle d'arguments de fonction.

let maxhash = max << hash
// The following is acceptable because the argument for maxhash is explicit:
let maxhash obj = (max << hash) obj

Cas 3 : ajout d'un paramètre supplémentaire inutilisé. Étant donné que cette expression n'est pas assez simple pour la généralisation, le compilateur émet l'erreur de restriction de valeur.

let emptyList10 = Array.create 10 []
// Adding an extra (unused) parameter makes it a function, which is generalizable.
let emptyList10 () = Array.create 10 []

Cas 4 : ajout de paramètres de type.

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)

Dans le deuxième cas, la valeur est une fonction de type, qui peut être utilisé pour créer des valeurs de différents types, par exemple les suivantes :

let intLists = arrayOf10Lists<int>
let floatLists = arrayOf10Lists<float>

Voir aussi

Référence

Inférence de type (F#)

Génériques (F#)

Paramètres de type résolus statiquement (F#)

Contraintes (F#)