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.