Tipos flexíveis (F#)
A anotação de tipo flexível indica que um parâmetro, variável ou valor tem um tipo que seja compatível com um tipo especificado, onde a compatibilidade é determinada pela posição em uma hierarquia e orientada a objeto de classes ou interfaces.Tipos flexíveis são úteis especialmente quando a conversão automática aos tipos mais altos na hierarquia de tipos não ocorre, mas você ainda deseja habilitar sua funcionalidade trabalhar com qualquer tipo na hierarquia ou de qualquer tipo que implementa uma interface.
#type
Comentários
Na sintaxe anterior, type representa um tipo base ou uma interface.
Um tipo flexível é equivalente a um tipo genérico que tem uma restrição que limita os tipos permitidos para tipos que são compatíveis com o tipo de base ou interface.Ou seja, as duas linhas de código a seguir são equivalentes.
#SomeType
'a when 'a :> SomeType
Tipos flexíveis são úteis em vários tipos de situações.Por exemplo, quando você tem uma função de ordem superior (uma função que usa uma função como um argumento), muitas vezes é útil ter a função retornar um tipo flexível.No exemplo a seguir, o uso de um tipo flexível com um argumento de seqüência na iterate2 permite que a função de ordem superior para trabalhar com funções que geram seqüências, matrizes, listas e qualquer outro tipo enumerable.
Considere as duas funções a seguintes, um dos quais retorna uma seqüência, o outro que retorna um tipo de flexível.
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])
Como outro exemplo, considere a Seq.concat a função de biblioteca:
val concat: sequences:seq<#seq<'T>> -> seq<'T>
Você pode passar qualquer uma das seguintes seqüências de enumerable para esta função:
Uma lista de listas
Uma lista de matrizes
Uma matriz de listas
Uma matriz de seqüências
Qualquer outra combinação de seqüências de enumerable
O seguinte código usa Seq.concat para demonstrar os cenários em que você pode suportar usando tipos flexíveis.
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
A saída é da seguinte maneira.
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; ...]
No F#, como em outras linguagens orientadas a objeto, existem contextos em que tipos derivados ou tipos que implementam interfaces são automaticamente convertidos em um tipo de interface ou um tipo base.Essas conversões automáticas ocorrem nos argumentos diretos, mas não quando o tipo é em uma posição subordinada, como parte de um tipo mais complexo como, por exemplo, um tipo de retorno de um tipo de função, ou como um argumento de tipo.Assim, a notação de tipo flexível é útil principalmente quando o tipo que você está aplicando o que ele faz parte de um tipo mais complexo.