Partager via


Généralisation automatique

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 fonctionnent avec plusieurs types lorsque cela est possible.

Généralisation automatique

Le compilateur F#, lorsqu’il effectue une inférence de type sur une fonction, détermine si un paramètre donné peut être générique. Le compilateur 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 type est déduit comme générique.

L’exemple de code suivant illustre une fonction que le compilateur déduit être générique.

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

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

Le type indique qu’il s’agit d’une fonction qui prend deux arguments du même type inconnu et retourne une valeur de ce même type. L’une des raisons pour lesquelles 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 à celui-ci a la signature 'a -> 'a -> bool. Tous les opérateurs ne sont pas génériques et si le code d’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é qu’il max est générique, il peut être utilisé avec des types tels que int, floatet 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 n’est 'a -> 'a -> 'apas 'a -> 'b -> 'a. Par conséquent, le code suivant génère une erreur, car les types ne correspondent pas.

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

La max fonction fonctionne également avec n’importe quel type qui prend en charge l’opérateur supérieur à celui-ci. Par conséquent, vous pouvez également l’utiliser sur une chaîne, comme indiqué dans le code suivant.

let testString = max "cab" "cat"

Restriction de valeur

Le compilateur effectue une généralisation automatique uniquement sur les 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 suffisamment limité pour être un type spécifique, mais n’est pas également généralisable. Le message d’erreur pour ce problème fait référence à cette restriction sur la généralisation automatique pour les valeurs comme restriction de valeur.

En règle générale, l’erreur de restriction de valeur se produit lorsque vous souhaitez qu’une construction soit générique, mais que le compilateur dispose d’informations insuffisantes pour la généraliser, ou lorsque vous omettez involontairement suffisamment d’informations de type dans une construction non générique. La solution à l’erreur de restriction de valeur consiste à fournir des informations plus explicites pour limiter plus complètement le problème d’inférence de type, de l’une des manières suivantes :

  • Limitez un type à être non 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érique pour définir une fonction générique, telle qu’une composition de fonction ou des arguments de fonction curried appliqués de manière incomplète, essayez de réécrire la fonction en tant que définition de fonction ordinaire.

  • Si le problème est une expression trop complexe à généraliser, faites-la dans une fonction en ajoutant un paramètre supplémentaire non utilisé.

  • 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 destinée à ê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érique pour définir une fonction générique. Dans cet exemple, la construction n’est pas pouvant être générée, car elle implique une application partielle des 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 dernier cas, la valeur devient une fonction de type, qui peut être utilisée pour créer des valeurs de nombreux types différents, par exemple comme suit :

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

Voir aussi