Partager via


Types flexibles (F#)

Une annotation de type flexible indique qu'un paramètre, une variable ou une valeur possède un type qui est compatible avec un type spécifié, où la compatibilité est déterminée par la position dans une hiérarchie orientée objet de classes ou d'interfaces. Les types flexibles sont particulièrement utiles lorsque la conversion automatique en types plus élevés dans la hiérarchie de types ne se produit pas, mais que vous souhaitez tout de même permettre à votre fonctionnalité de fonctionner avec n'importe quel type dans la hiérarchie ou n'importe quel type qui implémente une interface.

#type

Notes

Dans la syntaxe précédente, type représente un type de base ou une interface.

Un type flexible équivaut à un type générique contenant une contrainte qui limite les types autorisés en types qui sont compatibles avec le type de base ou d'interface. Ainsi, les deux lignes de code suivantes sont équivalentes.

#SomeType

'a when 'a :> SomeType

Les types flexibles sont utiles dans plusieurs types de situations. Par exemple, lorsque vous avez une fonction d'ordre supérieur (c'est-à-dire une fonction qui prend une fonction comme argument), il est souvent utile de définir la fonction de manière à ce qu'elle retourne un type flexible. Dans l'exemple suivant, l'utilisation d'un type flexible avec un argument de séquence dans iterate2 permet à la fonction d'ordre supérieur d'utiliser des fonctions qui génèrent des séquences, des tableaux, des listes et d'autres types énumérables.

Considérez les deux fonctions suivantes : l'une retourne une séquence, l'autre un type flexible.

let iterate1 (f : unit -> seq<int>) =
    for e in f() do printfn "%d" e
let iterate2 (f : unit -> #seq<int>) =
    for e in f() do printfn "%d" e

// Passing a function that takes a list requires a cast.
iterate1 (fun () -> [1] :> seq<int>)

// Passing a function that takes a list to the version that specifies a 
// flexible type as the return value is OK as is.
iterate2 (fun () -> [1])

Voici un autre exemple, celui de la fonction de bibliothèque Seq.concat :

val concat: sequences:seq<#seq<'T>> -> seq<'T>

Vous pouvez passer n'importe laquelle des séquences énumérables suivantes à cette fonction :

  • Une liste de listes

  • Une liste de tableaux

  • Un tableau de listes

  • Un tableau de séquences

  • Toute autre combinaison de séquences énumérables

Le code suivant utilise Seq.concat pour montrer les scénarios que vous pouvez prendre en charge à l'aide de types flexibles.

let list1 = [1;2;3]
let list2 = [4;5;6]
let list3 = [7;8;9]

let concat1 = Seq.concat [ list1; list2; list3]
printfn "%A" concat1

let array1 = [|1;2;3|]
let array2 = [|4;5;6|]
let array3 = [|7;8;9|]

let concat2 = Seq.concat [ array1; array2; array3 ]
printfn "%A" concat2

let concat3 = Seq.concat [| list1; list2; list3 |]
printfn "%A" concat3

let concat4 = Seq.concat [| array1; array2; array3 |]
printfn "%A" concat4

let seq1 = { 1 .. 3 }
let seq2 = { 4 .. 6 }
let seq3 = { 7 .. 9 }

let concat5 = Seq.concat [| seq1; seq2; seq3 |]

printfn "%A" concat5

La sortie est la suivante.

seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]

En F#, comme dans d'autres langages orientés objet, les contextes dans lesquels des types dérivés ou des types implémentent des interfaces sont automatiquement convertis en type de base ou en type d'interface. Ces conversions automatiques se produisent dans les arguments directs, mais pas lorsque le type est dans une position subordonnée, dans le cadre d'un type plus complexe tel qu'un type de retour d'un type de fonction, ou en tant qu'argument de type. Ainsi, la notation de type flexible est avant tout utile lorsque le type auquel vous l'appliquez fait partie d'un type plus complexe.

Voir aussi

Référence

Génériques (F#)

Autres ressources

Référence du langage F#